用机器码写hello world
本文最早发布于我的博客 http://yangwang.hk/?p=281,采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可,转载需声明。与原文有改动
[hr]闲的蛋疼,于是就有了这个无聊的想法
用汇编写程序就已经看起来很高大上了,那么直接用机器码呢?
其实机器码并没有想象中的那么困难,
机器码和汇编语言是一一对应的,因此知道了汇编语言,再学会转换的方法,就可以直接用机器码写程序了
本文将会分为两个部分,介绍如何用机器码写hello world
一、基础知识
  • 写一个怎样的程序
  • 计算机引导
  • mbr
  • x86
  • 实模式与保护模式
  • 逻辑地址
  • 显存与内存映射
二、写程序
  • 使用diskgenius写机器码
  • 运行程序

[hr]废话不多说,让我们先从写一个怎样的程序开始
【你这不是废话吗,标题都写了是hello world!】
不不不,没那么简单,写完了程序在哪儿运行也是一个值得思考的问题
【这有啥子好思考的?双击运行呗!】
NO,没有那么简单。要知道cpu只能从内存中执行我们的程序,想让计算机运行程序,就需要先将程序放入内存。当你在windows里双击一个可执行文件时,操作系统会帮助我们将程序放入内存,再由计算机执行
但是若想要操作系统听你的话,你就必须按照操作系统指定的格式来编写程序。如果你不按照系统的要求来,他只会弹出来一个框,告诉你“xxx不是win32应用程序”
然后你就只有瞄着屏幕哈气了(我们数学老师的口头禅)
我们的目标是在屏幕上显示出一行hello world,考虑这些会大大增加实现的难度。
为了更加方便编(zhuang)程(B),扔掉该死的操作系统就好了
不过抛弃了操作系统,上面的问题还是没有解决:如何将程序放入内存?
要解决这个问题,我们必须从计算机的引导过程说起.
你可能已经知道,引导的英文是是“boot”,如果你比较冲动的去查了一下,你会发现它本来的意思是靴子。。。没错,就是靴子
原来,这里的boot是bootstrap(鞋带)的缩写,它来自一句谚语:
“pull oneself up by one’s bootstraps”,意思是拽着鞋带把自己拉起来,这句话牛顿爷爷听了肯定很不高兴。
在计算机刚刚发明的时候,工程师是灰常纠结的,为什么呢?要知道,计算机是从RAM里头执行程序的,工程师纠结的问题和我们差不多,如何把程序放入RAM。
这可以由一个程序完成,但是这样就意味着:必须先运行一段程序计算机才能启动,计算机要运行程序,又必须启动……于是就死循环了。。。
工程师需要想尽办法,把程序装入RAM,计算机才能启动,而这个过程他们叫做“拉鞋带”,久而久之就叫“boot”了。
之后,人们发明了ROM,人们将开机程序存在ROM里,这样每次开机,计算机就自动从ROM里执行这段开机程序。
这块芯片里的程序叫做”基本输出输入系统”(Basic Input/Output System),简称为BIOS。BIOS程序会将引导程序放入内存,然后让cpu执行引导程序,自己就去睡觉了
我们只需要将我们的hello world当做引导程序,其他的交给BIOS就行了。
BIOS实际上是将硬盘(严格的说是储存介质,包括光盘)上的第一个扇区(共512字节)放入内存,这个扇区叫做MBR( Master Boot Record,主引导记录 )
MBR分为三个部分,分别是
  • 466字节的引导程序
  • 64字节的分区表
  • 2字节的校验位
引导扇区是用来放引导程序的(有引导的需要的话),我们这个程序没有什么需要引导的,因此直接将hello world放入这里就行了。466字节看起来很少,实际上对于显示hello world是绰绰有余的(我们仅是用了几十字节)
分区表没什么好说的,用来硬盘的分区信息。因为只有64字节,因此保存的数据是很有限的,这也是为什么一块硬盘上最多只能有四个主分区的原因——第五个主分区放不下…
最后的校验位很重要,正确的值是55aa(不然说明这个不是MBR)。
BIOS程序是很负责的,它在把这个扇区的内容复制到内存中后,会检查这两个字节,当它为55AA的时候,BIOS才会放心的睡大觉(不然说明这不是引导信息,BIOS会继续按照你设置的引导顺序检查别的驱动器,比如光驱)
x86
1978年6月8日,Intel发布了新款16位微处理器“8086”,也同时开创了一个新时代:x86架构诞生了。
这是一个复杂指令集(于此相对有“精简指令集”)
尽管过了这么多年,但直到今天,为了保证电脑能继续运行以往开发的各类应用程序,Intel公司所生产的所有CPU仍然继续使用(兼容)X86指令集。
(由于x86的效率实在是太低,inter早就想扔掉x86。但是在2003年,竞争对手AMD首先推出了64位cpu,x86-64,也就是传说中的x64,兼容x86。inter没有办法,只好也与2004年推出了兼容x86的64位cpu)
上面都扯远了,现在再说一说实模式与保护模式
保护模式与实模式相对应。在80286以前,CPU只有实时模式,在286之后增加了保护模式。保护模式下有保护机制,可以防止程序在运行的时候胡乱修改内存(比如数组越界,如果在实模式下,系统就可能崩溃)
但为了保证兼容(又是讨厌的兼容),80286及以后的CPU会首先进入实模式,然后通过切换机制再进入到保护模式。
在实模式下面,所有寄存器都是16位的(哪怕是64位的cpu也一样),在切换到保护模式下后,才会变成32(64)位。
我们只不过想显示一行hello world,就用实模式足够了,简单才是王道嘛!
来自 Linux
 
2015-1-18 19:31:13
相对论万岁(作者)
1楼
前面讲到的都是一些需要了解的基础知识
不过知道上面那些东西还远远不够
本文就将介绍一些编程中必须知道的知识。
我们知道了BIOS程序会将引导程序加载入内存,问题是加载到哪儿呢?
这个问题值得讨论,因为不知道程序在内存中的位置,我们就很难控制程序的执行。
答案是0x7c00
为什么会选择这样一个奇葩地址,而不是0x0000之类的方便易记的地址,当然是有原因的。这里就不介绍了,有兴趣希望了解的可以使用百度、谷歌搜索。
086的寻址方式
因为8086的cpu是16位的,理论上来说,16位的总线最多能访问2^16=64KB的内存(16根总线一共可以表示65536种不同的地址,因为内存是字节寻址,一个字节1Byte,所以就是64KB),这么点内存在当时也是捉襟见肘的。
为了能让cpu控制更多的内存,就必须增加控制总线的数量,于是乎8086就设计出了20位的寻址总线,也就是2^20=1MB,这样内存总算够用了。但是寄存器还是16位的呀,怎么办呢。8086就使用了一种非常奇葩的方法,一个寄存器不够,那我们就用两咯,一个叫做段地址,一个则是偏移地址
我们把一栋楼房当作一块内存,一共有2000个房间,房间按照0000-2000顺序编号(这就是物理地址)。
但是由于偷工减料,现在一个门牌号只能写三个数字。为了解决门牌号的问题,我们可以把这栋楼分成多个单元(段)一个单元又分成多个房间。
假设一个单元有100个房间,那么0106号房间就是“二单元006”,其中二单元写在门牌A上,006则写在B上,这就是逻辑地址。
知道了逻辑地址,我们就可以把它换成物理地址,你应该看得出来,换算关系就是把门牌A的值-1,然后乘100,再加上门牌B的值。(2-1)*100+006=0106
在CPU中,门牌A就是段寄存器,保存的地址叫做段地址。门牌B则是指针寄存器,保存的值叫偏移地址。
在访问内存时,cpu将段地址左移四位,再加上偏移地址,就合成了20位的地址
如段地址为0x72,偏移地址为0x25,段地址偏移后的值为0x720,加上偏移地址就成了0x745.
因为16位最大是65536,也就是64kb,因此我们可以得出结论,每一个段最多是64k。
和上面楼房的地址不同,段是可以重叠的,比如你可以将0x100-0x1ff作为一个段,将0x101-0x200作为另一个段。也就是说一个物理地址对应多个逻辑地址
如何显示内容
在c语言中,我们可以很简单的使用printf来输出一个字符串
但是在汇编下可没有这东西。
我们需要直接控制显示器按照我们的需要显示信息。
如果你凑近看的话,就会发现显示器上有很多个小点,我们只需要控制这些小点,让他们以字符的样子显示出来就行了
你可能会跳起来说,这怎么控制!
别急嘛,且听我慢慢说。
对显示器来说,文字也好,图形也好,都是一堆堆的像素点,区别仅仅是他们的组合不同而已。但是如果让我们来控制这些像素,就显得非常困难并且麻烦了。
于是人们就制造了一个专门的硬件,让他来负责控制这些像素点。这就是显卡
显卡有一块内存,叫显存,我们只需要把字符写入显存,显卡就会帮助我们将他显示在屏幕上。字符使用的是ASCII码
由于显存是位于显卡上的,如果访问显存也需要通过显卡这个外围设备打交道的话,就显得有些麻烦了,毕竟多了一道手续了嘛!
于是聪明的人们想出来一个叫做“映射”的方法。即将显存当作内存(毕竟都是ram),映射到内存地址中,这样就可以直接使用cpu访问显存了。这块“内存”和普通内存没什么两样,唯一的区别就是你写在里面的数据显卡经过一定的处理显示在屏幕上而已。
086下0X00000-0X9FFFF是留给普通内存的,后面的0XF0000-FFFFF则是存放BIOS的芯片。中间0XA0000-EFFFFF的320k字节,则由各种外围设备提供,比如显卡。
显存的范围是0xB8000-0XBFFF,除非你的显卡出了毛病。否则这个地址肯定是可以访问的!
由于历史原因,显卡在启动后,会自动的被初始化为80×25的文本模式,一共两千个字符。
0xB8000就对应屏幕的左上角第一个字符,然后是往右依次类推
和用c语言的printf输出不同的是,一个字符并不是占用一个字节,而是两个。
第一个字节是字符的ASCII,第二个字节指定了字符的显示属性,比如颜色,是否闪烁(某些人想尽各种办法希望给命令行窗口中的文字加上不同的颜色,殊不知操作显存控制颜色竟是如此方便)
颜色分为前景色和背景色,格式如下
0__e7752c62922bf0b.png

RGB控制颜色,它们对应的意义都在下表中给出(数据摘自《X86汇编语言从实模式到保护模式》李忠、王晓波、余洁著,P51)
0__84fb192a266f037.png

比如我们要黑底白字无闪烁(也就是用的最多的模式),他的属性值就是
K=0,RGB=000;I=0,RGB=111,合起来就是00000111b
换成16进制则是0x07
假设我们需要显示一个黑底白字的无闪烁字母H,0xb8000的位置就是H的ASCII码(0x48),后面接着的0XB8001就是显示属性0x07.
你可能会好奇,屏幕上一片漆黑,啥都没有的时候显存里面是什么东西?
还能是什么?空格呗!
折叠评论
加载评论中,请稍候...
折叠评论
相对论万岁(作者)
2楼
到这里我们终于可以开始动手使用机器码写程序了
不过在此之前,我们还需要做一些准备。
因为我们选择将hello world写入mbr中,而这样会覆盖掉引导信息。如果你直接在你的硬盘上操作的话,你的系统就不能启动了(尽管我们可以使用修复软件修复引导记录,但是那样很麻烦)
你需要的仅仅是一个u盘,或者储存卡+读卡器(这里便使用的储存卡)
然后是一台支持usb启动的计算机
【我怎么知道支不支持?】
只要你不是老掉牙的几十年前的计算机,都是支持的
【我们u盘里有很多文件啊,弄了这个会不会造成数据丢失啊?】
不会!我们只会修改前面引导程序的部分,不会修改后面的分区记录,所以保存的数据是不会丢失的。如果你不放心可以先备份
当然,如果你的u盘是“引导盘”,比如xxxPE装机的,由于修改了引导程序,自然就会失效……所以这类u盘谨慎尝试(大不了再制作一次嘛,或者在写程序时进行扇区备份)
如果你不想用u盘折腾,那也行。虚拟机的虚拟磁盘一样是支持的
至少vhd,vmdk之类的格式都是支持的。
我们需要对扇区直接进行操作,这需要专门的软件,但是我找了一圈,有两种编辑器,一种是好用的,一种是不好用的,在好用的里面却只有一种,那就是死贵的!
winhex貌似可以免费试用,但是界面是英文的,diskgenius是中文的,可是目前最新版不交钱不能编辑扇区
好在貌似diskgenius以前的版本是可以免费编辑扇区的,而我以前下载过,今天又从硬盘中把它翻了出来,你可以“点我下载
下载后直接运行就是了,你会得到这样的一个界面


插上u盘,然后选择你要写程序的硬盘(注意是硬盘不是分区)
在卷标的下面有一个“扇区编辑”,点一下,就会成这个样子


如果你选择使用虚拟机
请创建一个固定大小的单文件磁盘,不需要多大,10MB都够用了!,内存更不用说,1M足矣!操作系统选择other(其他),并且不需要添加镜像!
然后在diskgenius里面的菜单“硬盘”->“打开虚拟硬盘文件”,选择你的虚拟磁盘文件,后面的操作都是一样的!

注意右边的“Error loading operating system”是不是有些熟悉呢?没错,你选择了没有引导记录的扇区就会显示这个,其实这样的错误信息也是MBR程序显示出来的。
往下拉一点,你应该看得到“55AA”,这就是传说中的校验位,也就是512字节的地方。
现在回到最开始的地方,我们开始写程序!
注意:如果你不想手动输入而是复制机器码,请复制后右键->写入->写入hex即可
机器码可以通过http://ref.x86asm.net/coder.html#xF3查询
这里也提供一个pdf版的inter开发文档点我下载
[hr]我们先来整理一下思路
为了便于操作显存,我们将显存从0xb8000开始作为一个段,也就是0xb800:0x0000,将它放入段寄存器ES(其实DS也行,不过后面DS有其他的用途)
完成这一步的机器码是:B8 00 B8,8E C0


像这样。
B8是mov指令,它将0xb800放入ax寄存器(因为是小端模式,00是低位,放在高位上)
然后我们将ax的数据送入ES寄存器,8E是mov指令(mov有好多不同的指令……),C0则代表AX。
然后,我们将从把屏幕“清屏”,就是将屏幕用黑底的空格填充,空格的ascii是0x20,黑底则是0x07。放在一起就是0x0720(小端)
我们使用循环填充空格的方法,需要在cx寄存器中放入循环的次数2000次
它是:B9 D0 07(2000的15进制是0x07D0)
B9=B8+1,1说明目标是CX(ax是+0,所以还是B8)
然后我们将0放到bx中当作偏移地址(这里有点像c语言中的数组,第一个元素的地址就是段地址,下标则是偏移地址)
机器码为BB 00 00
BB=B8+3,3就是BX,后面四个0不说你都知道。
接下来就是循环体了:26 C7 07 20 07
26是说明以ES作为段寄存器,前面我们已经将他改成了0xB800。
C7是把立即数放入内存的mov指令,07代表用BX提供偏移地址,2007则是数据
然后是81 C3 02 00
81是add指令,c3代表BX,0200就是0x02,说明这里将bx自增了2。因为上面一次性传送了2字节,所以偏移地址也要往后移动两字节
循环体到此结束,下面是E2  F5
E2是LOOP指令,后面的0xF5则是偏移地址,他是1Byte的有符号数,负数说明往前退,F5就会退到前面26 C7 07的地方,每执行一次,CX自动减1,直到cx为0。也就是说,会执行2000次。
接下来我们将为把字符串写入显存做准备
因为指令和字符串这样的数据是放在一起的(这种指令和数据放在一起的叫做冯诺依曼架构,还有一种数据和指令分别放在不同内存上的叫做哈弗架构。一般的计算机都是冯诺依曼架构,而CPU内的Catch则是哈弗架构)
为了将字符串写入到显存,我们需要知道字符串的位置
我们把程序加载开始的部分当作段地址,并且将它放入DS
B8 C0 07,结合前面的你应该知道这将0x07c0放入了ax,然后是8E D8
他将AX的值放入DS中
接着是FC,这是cld指令,因为我们后面需要传送字符串,因此需要将它清0,指定字符串的传送方向。
后面是BE 2A 00BF 00 00
这都是传送指令,BE是传送到SI,BF则是传送到DI,SI里面放的是字符串的偏移量,DI里面放的是显存中的偏移量,因为我们是从第一个字符开始写,因此偏移量就是0.
最后,我们需要再次指定循环的次数
B9 0C 00,他将hello world的长度放入cx,
0x0C是12个,即“Hello World!”一共十二个字符
倒数第二步,我们将字符串一个个的循环传送到显存,机器码为F3 5A
F3 A5代码字符串传送,就是rep movs。Move (E)CX words from DS:[(E)SI] to ES:[(E)DI].。他会一直执行到CX为0,也就是把hello world全部写入显存。
最后一步。cpu在执行完了上面步骤后并不会停下来,如果你不进行干预的话他会一直执行下去。这不是我们希望看到的结果,因此我们需要让cpu有事情干——死循环!
死循环的机器码为FD FF。其中FD是jmp指令,FF表示往前退1字节,也就是FD的位置。这样cpu就会不停的在这一句上跳转,也就是死循环了
程序到这里就算完了
【喂喂喂,我们怎么没有看到hello world字符串啊!】
急什么,这不是留在后面来设计吗?
前面已经详细的说了显存中显示字符的方法,这里就直接给出字符串了
48 8F 65 8F 6C 8F 6C 8F 6F 8F 20 07 57 02 6F 02 72 02 6C 02 64 02 21 02
我个人喜欢让hello 闪烁,然后world用绿色显示出来,黑底白字闪烁的属性是8F(可以看到前面每隔一个字符就是一个8F),黑底绿色不闪烁则是02
48 65 6c等则是hello world的ASCII
写到这里,我们的程序就算彻底搞定了
到这里你就应该明白DI的值为什么是2A了,你可以数数,字符串的第一个字节的偏移地址正是0x2A(以程序存放的地址作为段)
下面给出本程序完整的机器码
B800B88EC0B9D007BB000026C707200781C30200E2F5B8C0078ED8FCBE2A00BF0000B90C00F3A5E9FDFF488F658F6C8F6C8F6F8F200757026F0272026C0264022102
你也可以下载这个包含机器码的txt
写好了后是这个样子


红色代表数据被修改了,中间那个BE只是恰好和原来的数据一样罢了
最后点击“扇区编辑”左下边的保存即可

运行程序

搞了这么久,终于可以运行我们的程序了。
对使用u盘的等引导的人
首先你需要关闭计算机,别拔了u盘,然后再开机,刚刚出画面时根据提示按相关的按键。通常是F2,del,esc等等,总之你需要选择引导的磁盘(不清楚的可以百度,或者回复问我)
选择U盘启动,然后略等一下!就是见证奇迹的时刻!


对于使用虚拟机的人
你只需要启动电源,然后就可以看结果了!
折叠评论
加载评论中,请稍候...
折叠评论
3楼
为楼主逝去的头发表示惋惜……太不容易了。

文章详尽介绍了x86机器码编程的几乎所有注意事项,涵盖了从BIOS到实模式的几乎所有常识,是不可多得的一篇好文。

提个问题:如果我想用C写一个程序(包含标准输入输出),遍译成机器码,并让MBR自动跳转到我的程序去执行(而不使用操作系统),应该怎么做?
折叠评论
1
加载评论中,请稍候...
折叠评论
相对论万岁(作者)
4楼
引用 novakon:
为楼主逝去的头发表示惋惜……太不容易了。

文章详尽介绍了x86机器码编程的几乎所有注意事项,涵盖了从BIOS到实模式的几乎所有常识,是不可多得的一篇好文。

提个问题:如果我想用C写一个程序(包含标准输入输出),遍译成机器码,并让MBR自...
很抱歉,你的想法可能很难实现了…
因为标准输入输出这种东西实际上是调用系统的API实现的
也就是说,你不可能脱离系统去运行!
还有内存相关的malloc之类的也是一样。
不同平台上看似调用都一个样,其实底层的实现是完全不同的!要运行的话这些函数需要你自己实现!
不过写一个mbr执行其他扇区的程序还是可能的(并且不难)
折叠评论
加载评论中,请稍候...
折叠评论
2015-01-19 05:23:29
5楼
引用 相对论万岁:
很抱歉,你的想法可能很难实现了…
因为标准输入输出这种东西实际上是调用系统的API实现的
也就是说,你不可能脱离系统去运行!
还有内存相关的malloc之类的也是一样。
不同平台上看似调用都一个样,其实底层的实现是完全不同的!要运行的话这些...
原来你已经发过来了,觉得可以加学术分了
折叠评论
加载评论中,请稍候...
折叠评论
6楼
引用 相对论万岁:
到这里我们终于可以开始动手使用机器码写程序了
不过在此之前,我们还需要做一些准备。
因为我们选择将hello world写入mbr中,而这样会覆盖掉引导信息。如果你直接在你的硬盘上操作的话,你的系统就不能启动了(尽管我们可以使用修复软件...
瞬间觉得用C#写的程序效率低到爆炸
折叠评论
加载评论中,请稍候...
折叠评论
7楼
引用 相对论万岁:
到这里我们终于可以开始动手使用机器码写程序了
不过在此之前,我们还需要做一些准备。
因为我们选择将hello world写入mbr中,而这样会覆盖掉引导信息。如果你直接在你的硬盘上操作的话,你的系统就不能启动了(尽管我们可以使用修复软件...
有没有办法用C语言写呢?
折叠评论
加载评论中,请稍候...
折叠评论
8楼
楼主的文章牛D~!, 机器码和汇编  启动方式还有内存和显存的映射 都非常详细[s::funk:]
折叠评论
加载评论中,请稍候...
折叠评论
9楼
引用 novakon:
为楼主逝去的头发表示惋惜……太不容易了。

文章详尽介绍了x86机器码编程的几乎所有注意事项,涵盖了从BIOS到实模式的几乎所有常识,是不可多得的一篇好文。

提个问题:如果我想用C写一个程序(包含标准输入输出),遍译成机器码,并让MBR自...
想办法把指令数据装内存里面再跳转过去
折叠评论
加载评论中,请稍候...
折叠评论
10楼
引用 novakon:
为楼主逝去的头发表示惋惜……太不容易了。

文章详尽介绍了x86机器码编程的几乎所有注意事项,涵盖了从BIOS到实模式的几乎所有常识,是不可多得的一篇好文。

提个问题:如果我想用C写一个程序(包含标准输入输出),遍译成机器码,并让MBR自...
标准输入输出,像printf之类的函数得自己写。
用C写的话,用GCC编译比较方便。编译完了把所有的中间文件链接到一起,用objcopy可以把代码部分提取出来,成为一个只包含机器码的文件。
折叠评论
加载评论中,请稍候...
折叠评论
11楼
有本书叫《Orange's一个操作系统的实现》,其中详细介绍了从头写出一个32位操作系统的过程。可惜这本书似乎已经绝版了。
折叠评论
加载评论中,请稍候...
折叠评论
12楼
引用 金星凌日:
标准输入输出,像printf之类的函数得自己写。
用C写的话,用GCC编译比较方便。编译完了把所有的中间文件链接到一起,用objcopy可以把代码部分提取出来,成为一个只包含机器码的文件。
GCC可以编译出X86机器码吗?
折叠评论
加载评论中,请稍候...
折叠评论
13楼
小白求教:用uefi启动的机器怎么弄?
折叠评论
加载评论中,请稍候...
折叠评论
14楼
引用 张静茹:
GCC可以编译出X86机器码吗?
可以啊。一般情况下生成的不都是x86机器码吗?
折叠评论
加载评论中,请稍候...
折叠评论
2015-01-20 07:23:44
15楼
引用 金星凌日:
可以啊。一般情况下生成的不都是x86机器码吗?
完全不是
折叠评论
加载评论中,请稍候...
折叠评论
16楼
引用 张静茹:
完全不是
不是吗?那是什么?我这里的GCC生成的确实是x86或x86-64的程序啊。
折叠评论
加载评论中,请稍候...
折叠评论
17楼
引用 金星凌日:
不是吗?那是什么?我这里的GCC生成的确实是x86或x86-64的程序啊。
你那个程序只能在操作系统下执行
折叠评论
加载评论中,请稍候...
折叠评论
18楼
引用 张静茹:
你那个程序只能在操作系统下执行
看来是我理解错你的意思了。
用GCC生成可执行程序之后,用objcopy可以提取出其中的代码部分,生成一个不需要操作系统的纯机器码文件。只是如果用C语言的话,标准库函数都得自己写,因为默认的库函数都只能在操作系统下运行。
折叠评论
加载评论中,请稍候...
折叠评论
19楼
引用 相对论万岁:
前面讲到的都是一些需要了解的基础知识
不过知道上面那些东西还远远不够
本文就将介绍一些编程中必须知道的知识。
我们知道了BIOS程序会将引导程序加载入内存,问题是加载到哪儿呢?
这个问题值得讨论,因为不知道程序在内存中的位置,我们就...
显存的范围是0xB8000-0XBFFF,除非你的显卡出了毛病。否则这个地址肯定是可以访问的!
更正 显卡的范围是0XB8000-0XBFFFF
折叠评论
加载评论中,请稍候...
折叠评论
20楼
在实模式下面,所有寄存器都是16位的
这句话不太对吧。在实模式下可以访问32位的寄存器。如果实模式下只能访问16位寄存器,那就没办法切换到保护模式下了,因为cr0寄存器是32位的。
在切换到保护模式下后,才会变成32(64)位。
那个64位的模式不叫保护模式,叫长模式。
折叠评论
加载评论中,请稍候...
折叠评论
21楼
机器码
B800B88EC0B9D007BB000026C707200781C30200E2F5B8C0078ED8FCBE2A00BF0000B9FF00F3A5E9FDFF2102220223022402250226022702280229022A022B022C022D022E022F0230023102320233023402350236023702380239023A023B023C023D023E023F0240024102420243024402450246024702480249024A024B024C024D024E024F0250025102520253025402550256025702580259025A025B025C025D025E025F0260026102620263026402650266026702680269026A026B026C026D026E026F0270027102720273027402750276027702780279027A027B027C027D027E027F02A102A202A302A402A502A602A702A802A902AA02AB02AC02AD02AE02AF02B002B102B202B302B402B502B602B702B802B902BA02BB02BC02BD02BE02BF02C002C102C202C302C402C502C602C702C802C902CA02CB02CC02CD02CE02CF02D002D102D202D302D402D502D602D702D802D902DA02DB02DC02DD02DE02DF02E002E102E202E302E402E502E602E702E802E902EA02EB02EC02ED02EE02EF0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055AA

效果
DSCN8090.JPG
折叠评论
加载评论中,请稍候...
折叠评论
22楼
引用 张静茹:
机器码
B800B88EC0B9D007BB000026C707200781C30200E2F5B8C0078ED8FCBE2A00BF0000B9FF00F3A5E9FDFF2102220223022402250226022702280...
用ce改游戏的时候经常看这些
折叠评论
加载评论中,请稍候...
折叠评论
相对论万岁(作者)
23楼
引用 张静茹:
显存的范围是0xB8000-0XBFFF,除非你的显卡出了毛病。否则这个地址肯定是可以访问的!
更正 显卡的范围是0XB8000-0XBFFFF
感谢更正……
还有文中的某处“15进制”应为16进制
折叠评论
加载评论中,请稍候...
折叠评论
相对论万岁(作者)
24楼
引用 金星凌日:
这句话不太对吧。在实模式下可以访问32位的寄存器。如果实模式下只能访问16位寄存器,那就没办法切换到保护模式下了,因为cr0寄存器是32位的。
那个64位的模式不叫保护模式,叫长模式。
对……说法有问题。实模式是指内存寻址方式和086相同,内存有1mb限制。但是32位寄存器(如eax)也是可以访问的。
保护模式就是32位模式,文中这里确实有问题。
其实我才刚刚开始学这个,还没看到保护模式那儿……[s::lol]
多多学习哈[s::handshake]
折叠评论
加载评论中,请稍候...
折叠评论
相对论万岁(作者)
25楼
引用 张静茹:
机器码
B800B88EC0B9D007BB000026C707200781C30200E2F5B8C0078ED8FCBE2A00BF0000B9FF00F3A5E9FDFF2102220223022402250226022702280...
你这个不会是纯手打的吧……
折叠评论
加载评论中,请稍候...
折叠评论
2015-01-21 06:41:31
26楼
引用 相对论万岁:
你这个不会是纯手打的吧……



private void button1_Click(object sender, EventArgs e)
        {
            textBox2.Text = "";
            int[] a = new int[0xff];
            for (int i = 0; i < a.Length; i++)
            {
                a[i] = i;
            }


            foreach (var item in a)
            {
                //textBox2.Text += ((int)item).ToString("X2") +"02";
                textBox2.Text += ((int)item).ToString("X2") +"02";
            }
        }
折叠评论
加载评论中,请稍候...
折叠评论
27楼
引用 相对论万岁:
你这个不会是纯手打的吧……
还有什么办法可以显示出更长的文本吗?
B9 0C 00 已经被我改成B9FF00了可以改成B9FFFF吗
折叠评论
加载评论中,请稍候...
折叠评论
相对论万岁(作者)
28楼
引用 张静茹:
还有什么办法可以显示出更长的文本吗?
B9 0C 00 已经被我改成B9FF00了可以改成B9FFFF吗
一个循环,就可以把ascii循环显示出来了啊
折叠评论
加载评论中,请稍候...
折叠评论
2015-01-22 11:16:30
29楼
看了楼主的文章,想起我做过的一道题目
折叠评论
加载评论中,请稍候...
折叠评论
2015-01-24 11:56:49
2015-1-24 11:56:49
30楼
引用 金星凌日:
有本书叫《Orange's一个操作系统的实现》,其中详细介绍了从头写出一个32位操作系统的过程。可惜这本书似乎已经绝版了。
是于渊的书吧,应该没有绝版,前两天在狗东上见过
折叠评论
加载评论中,请稍候...
折叠评论
31楼
引用 管你员:
是于渊的书吧,应该没有绝版,前两天在狗东上见过
刚才又去看了下,纸质的却是没有了,看到的电子版,而且报价是¥34.50
折叠评论
加载评论中,请稍候...
折叠评论
2015-01-30 12:01:19
2015-1-30 12:01:19
32楼
引用 novakon:
为楼主逝去的头发表示惋惜……太不容易了。

文章详尽介绍了x86机器码编程的几乎所有注意事项,涵盖了从BIOS到实模式的几乎所有常识,是不可多得的一篇好文。

提个问题:如果我想用C写一个程序(包含标准输入输出),遍译成机器码,并让MBR自...
楼主的头发掉了?
折叠评论
加载评论中,请稍候...
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

插入资源
全部
图片
视频
音频
附件
全部
未使用
已使用
正在上传
空空如也~
上传中..{{f.progress}}%
处理中..
上传失败,点击重试
{{f.name}}
空空如也~
(视频){{r.oname}}
{{selectedResourcesId.indexOf(r.rid) + 1}}
ID:{{user.uid}}
{{user.username}}
{{user.info.certsName}}
{{user.description}}
{{format("YYYY/MM/DD", user.toc)}}注册,{{fromNow(user.tlv)}}活动
{{submitted?"":"投诉"}}
请选择违规类型:
{{reason.description}}
支持的图片格式:jpg, jpeg, png