目录

  • 回顾
  • 今日目标
  • Boot Sector 与内存的关系
    • 打印任意内存位置上的内容(以 16 进制输出)
    • 物理寻址的应用
      • 找到 Boot Sector 在内存中的前两个字节的位置

        • 寄存器

          • 寄存器的种类

            • 通用寄存器
            • 段寄存器
            • 指针寄存器
            • 索引寄存器
            • 控制寄存器
          • 段和段寄存器小结
        • 完成任务
  • 程序拆解及必要汇编指令
    • org 指令
    • jmp 指令
    • times 指令
    • pusha popa 指令
    • 条件控制指令
    • 其他必要的汇编指令
  • 总结
  • 参考链接

回顾

上一篇,我们讲到了以下内容:

  • 16-bit Real Mode 是 x86 系列 CPU 的一种工作模式,所有 x86 系列 CPU 启动时,都处于 16-bit Real Mode
  • 16 bit 系统中,只有 64 KB 内存可以被识别,CPU 一次只能处理 16 bit 的数据
  • 8086 架构,因为有 20 bit 的地址总线,所以寻址总量有 1 MB
  • 8086 架构的内存管理采用内存分段机制,内存分段将程序不同的部分加载到不同的不连续的内存中
  • 16-bit Real Mode 真实物理地址的计算公式为:物理地址 = 段地址左移 4 位 + 偏移地址
  • 中断可以让 CPU 暂时停止当前任务,执行我们指定的任务,然后回去执行原先的任务
  • 中断在 interrupt vector 中由一个数值做索引,interrupt vector 包含 interrupt service routines 的内存地址
  • 第一个 Hello World 程序使用中断,在屏幕上显示 HelloWorld 字符串

代码仓库。

今日目标

本篇,我们将结束 16-bit Real Mode 的内容,储备更多的知识,为下一篇开启 32-bit Protected Mode 做准备。

本篇内容主要以汇编代码为主,讲解一些必要的指令。我们会从一个打印 16 进制字符串的程序开始,将

  • boot sector 与内存的关系
  • 字符串定义
  • 堆栈的使用
  • 方法调用
  • 条件跳转
  • 文件包含
  • 物理内存寻址

这些内容一一覆盖到。

需要大家注意的是,我们现在的编程环境和编程的内容都有很大的局限性,所以不要将现在所讲的东西不加推敲直接应用到其他环境中,会造成不正确的结果。

比如现在的环境只是 8086 16-bit,那么不能认为通用寄存器就只是 AX,而不去考虑是否因为环境不同,寄存器会发生变化。32-bit 环境下,通用寄存器为 EAX,64-bit 环境下,为 RAX。

又比如目前的 boot sector 程序没有分段,同样不能认为其他所有的汇编程序都不用分段。

当前讨论的,是如何在 8086 架构下,从头写一个简易操作系统,一切都围绕这个目标来展开。

Boot Sector 与内存的关系

我们从研究 boot sector 程序与内存的关系开始。

回想之前,计算器启动,BIOS 做自检(专业术语叫 POST - Power-On Self-Test,详见这篇文章),然后读取存储介质上的前 512 个字节,如果 0x55 和 0xaa 出现在第 510 和 511 个字节上,BIOS 就认为这是一个正规的 boot sector,开始执行其代码,然后加载操作系统。

一切程序的执行,第一步要将程序加载到内存。那么 boot sector 被 BIOS 加载到内存的那一部分呢?

首先,肯定不是整个内存的起始位置(0x0)。因为之前说过,远在 BIOS 寻找 boot sector 之前,就已经做开始检测硬件等的操作。想必有一些必要的指令,已经被加载到内存最初的位置上。另外,还记得 interrupt vector table 以及 interrupt service routines (ISRs),这些都必须首先存在于内存,我们在 boot sector 中才有能力去调用中断,做相应的操作。

书中说,boot sector,会被 BIOS 固定加载到 0x7c00 这个位置上。

直接引用书上的内存示意图(来源:https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf)。

原书中作者写着写着打了一个问号在这里。本着怀疑一切的态度,我们要证实一下,boot secotr 是不是被加载到了 0x7c00 这个位置。

打印任意内存位置上的内容(以 16 进制输出)

我们有能力打印字符(上一个 HelloWorld 程序),那么要证实 boot sector 被加载到 0x7c00,只需要打印出该内存地址开始的连续一些字节,和 od 命令的输出做比对,就能知道书中是否正确。记住 16-bit Real Mode 下内存没有保护机制,所以,是可以访问或者写入到任意内存,目前也没有什么后果,大家可以自行尝试。

这里的关注点不要放在代码上,后面会对代码做深入讨论。

打印 16 进制的代码在 @cfenollosa 的 Github 上能找到。这里需要两个文件,一个是 print 方法,负责打印字符串,另一个,是 print_hex 方法,负责转换 16 进制数为字符串,然后调用 print 来打印。

print.asm:

print:pusha; keep this in mind:
; while (string[i] != 0) { print string[i]; i++ }; the comparison for string end (null byte)
start:mov al, [bx] ; 'bx' is the base address for the stringcmp al, 0 je done; the part where we print with the BIOS helpmov ah, 0x0eint 0x10 ; 'al' already contains the char; increment pointer and do next loopadd bx, 1jmp startdone:poparetprint_nl:pushamov ah, 0x0emov al, 0x0a ; newline charint 0x10mov al, 0x0d ; carriage returnint 0x10poparet

print_hex.asm:

; receiving the data in 'dx'
; For the examples we'll assume that we're called with dx=0x1234
print_hex:pushamov cx, 0 ; our index variable; Strategy: get the last char of 'dx', then convert to ASCII
; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.
; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40
; Then, move the ASCII byte to the correct position on the resulting string
hex_loop:cmp cx, 4 ; loop 4 timesje end; 1. convert last char of 'dx' to asciimov ax, dx ; we will use 'ax' as our working registerand ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zerosadd al, 0x30 ; add 0x30 to N to convert it to ASCII "N"cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'jle step2add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7step2:; 2. get the correct position of the string to place our ASCII char; bx <- base address + string length - index of charmov bx, HEX_OUT + 5 ; base + lengthsub bx, cx  ; our index variablemov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234; increment index and loopadd cx, 1jmp hex_loopend:; prepare the parameter and call the function; remember that print receives parameters in 'bx'mov bx, HEX_OUTcall printpoparetHEX_OUT:db '0x0000',0 ; reserve memory for our new string

利用 cfenollosa 代码,我们写一个简单的测试代码:

boot_sect_mem_chk.asm

org 0x7c00
mov dx, [0x7c00] ; 将内存地址 0x7c00 位置上的数据,写入到 dx 寄存器
call print_hex ; 调用 print_hex 方法输出 dx 中的内容jmp $  ; 跳转到当前地址(无限循环)%include "print.asm"
%include "print_hex.asm"times 510 - ($ - $$) db 0 ; 512 个字节中剩余字节全部填充 0
dw 0xaa55 ; 最后一个字节,是 0xaa55,让 BIOS 知道这是 boot sector

编译:

nasm -f bin boot_sect_mem_chk.asm -o boot_sect_mem_chk.bin

运行:

od 命令的输出做对比:

od -t x1 -A n boot_sect_mem_chk.bin

证实了 boot sector 被加载到 0x7c00 内存的说法。也可以写一个循环,输出前 N 个字符,对比输出与 od 命令是完全一致的。

不要忘了,x86 架构是小字节序,QEMU 中打印出来的每个字节,与 od 命令显示的是相反的。

物理寻址的应用

前一篇文章铺垫了很多关于 8086 架构物理地址计算的信息,现在,该尝试一下在代码中做应用,加深理解。

找到 Boot Sector 在内存中的前两个字节的位置

我们还是拿 boot_sect_mem_chk.asm 举例,顺便开始讲解一些必须知道的汇编指令。

我们的目标是,用前文所说的寄存器,换一种方式,使用段寄存器来找到 Boot Sector 的前两个字节的内容,打印出来。

org 0x7c00
mov dx, [0x7c00] ; 将内存地址 0x7c00 位置上的数据,写入到 dx 寄存器
call print_hex ; 调用 print_hex 方法输出 dx 中的内容jmp $  ; 跳转到当前地址(无限循环)%include "print.asm"
%include "print_hex.asm"times 510 - ($ - $$) db 0 ; 512 个字节中剩余字节全部填充 0
dw 0xaa55 ; 最后一个字节,是 0xaa55,让 BIOS 知道这是 boot sector

我们先讲一些完成这个任务的必要知识,然后完成这个任务,再然后,我们来拆解这个程序,进入汇编指令的讲解。

寄存器

如果有人问说计算机有哪些寄存器?你的回答只是计算机有 AX,BX,CX,DX 等浙西寄存器,然后就没有然后了。那么,在继续阅读下面的内容之前,请再次全面来了解一下寄存器。

最基本的寄存器描述如下:

由于 CPU 访问内存的操作会大大降低计算机运行效率,所以 CPU 内部内置了一些记忆单元用于临时储存要处理的小量数据,这些记忆单元,就被称之为寄存器。

下面展开讨论寄存器。

寄存器的分类

x86 架构下,按照寄存器的作用,可以把寄存器分为 5 大类,分别是:

  • 通用寄存器 General Registers
  • 段寄存器 Segment Registers
  • 指针寄存器 Pointer Registers
  • 索引寄存器 Index Registers
  • 控制寄存器 Control Registers

再一一展开。

- 通用寄存器 -

x86 架构的 CPU 包含 4 个通用寄存器,32 位架构下,这 4 个寄存器分别是:

EAXEBXECXEDX

相应的,我们现在讨论的 80806 16 位架构下,这 4 个寄存器分别是:

  • AX:‘A’ 代表 Accumulator,该寄存器也称为累积寄存器,通常用于做数学运算
  • BX:‘B’ 代表 Base,该寄存器也称为基址寄存器,可以用于存放临时值,也可以用于做内存寻址
  • CX:‘C’ 代表 Count,该寄存器也称为计数寄存器,通常用于做循环计数
  • DX:‘D’ 代表 Data,该寄存器也称为数据寄存器,通常用于输入输出的操作,同时也可以和 AX 配合用于大数值乘除的运算

- 段寄存器 -

对于段寄存器的讲解比较重要,粗体字加深理解

8086 CPU 有 3 个常用段寄存器,3 个附加段寄存器。

常用段寄存器分别是:

  • 代码段寄存器 Code Segment Register:代码段包含所有的可以被执行的指令;代码段寄存器存放代码段的起始内存地址(逻辑地址)
  • 数据段寄存器 Data Segment Register:数据段包含程序需要的所有数据,常量,字符串等;数据段寄存器存放数据段的起始内存地址(逻辑地址)
  • 栈段寄存器 Stack Segement Register:栈段包含方法调用所需的参数,方法的返回地址等数据;栈段寄存器存放栈段的起始内存地址(逻辑地址)

逻辑地址指的是 16-bit 地址,也是后面段和段寄存器小结中提到的段选择符。

例如,DS 的值是 0x6F70,那么 mov ax, [0x1000] 中的物理内存地址是

0x6F70 * 10H + 0x1000 = 0x6F700 + 0x1000 = 0x70700

附加段寄存器分别是:

ESFSGS。这些寄存器提供了额外储存数据的空间。通常,MOVSCMPS 等字符串操作的会用 ES。程序员也可以在代码中手动指定这些寄存器的使用。

- 指针寄存器 -

3 个指针寄存器分别是:

  • 指令指针寄存器 Instruction Pointer(IP):存放下一条指令的内存地址偏移量,与 CS 一起,[CS:IP] 提供下一条指令的真实物理地址
  • 栈指针寄存器 Stack Pointer(SP):存放栈中的当前数据的内存地址偏移量,与 SS 一起,[SS:SP] 提供当前数据的真实物理地址,获得当前位置上的数据
  • 基址指针寄存器 Base Pointer(BP):存放栈底的内存地址偏移量,与 SS 一起,[SS:BP] 提供当前方法参数的真实物理地址;同时,BP 还可以跟 DI,SI 这两个索引寄存器一起使用,用于物理地址计算,如 [BP + SI + 0x10]

- 索引寄存器 -

2 个指针寄存器分别是:

  • 源索引寄存器 Source Index(SI):常用于字符串操作的源索引
  • 目标索引寄存器 Destination Index(DI):常用于字符串操作的目标索引

不过实际中,这两个寄存器也常与段寄存器配合,进行物理地址寻址,如 [DS:SI],[ES:DI]。

- 控制寄存器 -

控制寄存器属于高级话题,不在讨论范围。有兴趣的同学参考这篇文章。

段和段寄存器小结

8086 架构中,内存段十分重要,所以我们把段和段寄存器拿出来做一个小总结。

关于每一个段的特征以及每个段与内存寻址的关系,总结如下:

  1. 8086 架构下,每个段容量最大 64 KB(216 Bits)
  2. 汇编中,任何段中的任一内存地址的寻址,都是相对于段的起始内存地址
  3. 一个段,总是开始于一个能被 16 整除(10 进制,或者 16 进制被 10H 整除)的内存地址上
  4. 任一段寄存器存放的,都是相应段的起始内存地址。段寄存器中的地址,被称为段选择符(Segment Selector 这里有详解),段选择符 * 16 (16 进制乘以 10H,二进制左移 4 位)之后的,才是段地址(所以,前文勘误,直接说段寄存器中储存的是段地址是错误的表述)
  5. 物理地址,或者线性地址,是通过 段地址 + 偏移量 计算得到

来自 Wiki 的图片,解释物理地址的计算(二进制形式)。

完成任务

好了,有了上面的铺垫,我们来使用段寄存器找到 boot sector 的前两个字节。

首先,我们讲 org 指令注释掉。汇编的注释,使用分号 ;

我们看一下注释掉之后是个什么情况,还能打印出我们需要的前两个字节吗?

答案是否定的。

为什么会出现这样的情况?

先看一下这条指令

mov dx, [0x7c00]

[] 操作符中的地址,都是一个相对于段起始位置的内存偏移量

当我们使用

mov dx, [0x7c00]

的时候,事实上,assembler 内部是这样处理这个内存寻址的

mov dx, [DS:0x7c00]

DS 是段寄存器,我们会使用段寄存器中的段选择符,乘以 16 再加上 0x7c00 这个偏移量来计算最后的物理地址。

那我们来看一下没有 org 指令的情况下,DS 寄存器的值是多少?

我们尝试讲 DS 的值打印出来,结果发现没有输出,意味着 DS 中没有存放任何值(没有值不代表是 0)。

因此,无法找到 boot sector 前两个字节是理所当然的。

解决方案就很简单了,存放一个段选择符到 DS 中即可。

前文说过,汇编中所有寻址,都是相对于段的起始地址而言。我们已经知道并证实 boot sector 代码会被加载到 0x7c00 这个位置,我们的目标是打印物理地址位 0x7c00 这个位置上的数据。

根据

物理地址 = 段选择符 * 10H + 偏移量

的公式,DS 的值应该是 0x7c0,偏移量应该是 0x0

0x7c0 * 10H + 0x0 = 0x7c00

因此,修改代码如下,即可得到 boot sector 前两个字节。

这里有一个点要说明一下,所有的段寄存器和索引寄存器都是不能直接写入的,mov ds, 0x7c0 无法编译通过,必须有一个中间过渡,所以用 ax 作为过渡,将值写入到 ds 中。

拓展部分
另外,再抛出一个问题,为什么不能设置 DS 寄存器的值为其他值,如 0x0,或者 0x100,然后设置偏移量到相应的值去获取前两个字符。

首先我目前的解释是,因为我们编译出来的是原始文件,意味着所有字节都是数据,那么将被加载到 1 个段中(一个段 64 KB,我们的数据只有 512 Bytes),段的起始逻辑地址,就应该被写入到 DS 中,所有的对于段中字节的寻址,都以 DS 为相对地址。
为了证实这一点,我们可以获取一下最后两个字节 0xaa55。按照公式,DS 为 0x7c0,偏移量为 0x1fe(第 510 和 511 个字节)。

可以成功获取到最后两个字节的内容。

当然,我试过将 DS 设置为 0x0,偏移为 0x7c00 会有很奇怪的现象出现,大家自行尝试。

程序拆解及必要汇编指令

拆解一下前文的程序,代码如下:

org 0x7c00
mov dx, [0x7c00] ; 将内存地址 0x7c00 位置上的数据,写入到 dx 寄存器
call print_hex ; 调用 print_hex 方法输出 dx 中的内容jmp $  ; 跳转到当前地址(无限循环)%include "print.asm"
%include "print_hex.asm"times 510 - ($ - $$) db 0 ; 512 个字节中剩余字节全部填充 0
dw 0xaa55 ; 最后一个字节,是 0xaa55,让 BIOS 知道这是 boot sector

【1】:org 指令,明确告诉 assembler 我们的 Boot Sector 代码被加载到 0x7c00 的位置
【2】:mov 指令,将相对于 0x0,偏移量为 0x7c00 内存位置上的值写入 dx
【3】:调用 print_hex 方法,打印出 dx 寄存器中的值
【4】:挂起 CPU
【5】【6】:包含两个打印方法的文件
【7】:除去该行指令以上所有指令的长度,除去最后两个字节的长度,其余位置全部填充 0
【8】:最后两个字节固定值,0xaa55

开始展开。

org 指令

这个解释只有英文才能区分了,不知道该怎么翻译才好,中文的翻译都是指令。各种资料对于 org 的解释,指出 org 不是一个 instruction,而是一个 directive。类似 C 语言中的 define

org 指令,在书中的解释是,明确告诉 assembler 我们的 Boot Sector 代码被加载到指定的位置(0x7c00)。但是范范这么一句话,感觉什么都没有讲明白。根据资料,assembler 内部,有一个 Location Counter(LC),它负责记录当前内存中下一个可以用于存放编译后指令的空位。

org 指令更改 LC 到指定的内存地址。例如这里的 0x7c00

当前的 LC 的值,可以使用之前看到过的 $ 符号来表示。

我们可以打印出来看一下。

没有使用 org 指令,LC 的值是 0000 (打印出来的乱码至今未理解是为什么)

使用了 org 指令,LC 的值是指定值。

LC 的值,是根据指令递增的,一条指令被编译存放入上一个 LC 的位置之后,LC 会增加这个指令的长度,准备存放下一个指令。

下图展示了 LC 在第一个 call print_hex 指令之后,递增了 3 个字节。

LC 作为概念大家明白就行。我试着找 LC 与内存寻址相关的资料,结果都没有找到。意味着 LC 对于我们理解内存寻址来说没有什么作用。

但是我们可以通过程序来的行为来进一步解释 org 指令到底干了什么。

我们可以打印寄存器中的值,那么,我就把所有寄存器的值全部打印出来,然后对比一下使用 org 指令和没有使用 org 指令前后的区别,看是否是因为 org 指令初始化了 DS 寄存器,让 mov dx, [0x7c00] 可以获取到相应数据。

对比结果如下图。

使用了 org 指令,除了 sp 是一个非 0 值,其他所有寄存器都被设置成了 0x0。因此,根据寻址公式,0x0 * 10H + 0x7c00 = 0x0 + 0x7c00 = 0x7c00,就可以获取到 boot sector 前两个字节的内容。

然后我将 org 指令注释掉,结果是各个寄存器全部是乱码。最后也无法获取到 boot sector 前两个字符的内容。

可以初步得出结论,org 指令除了资料上说的设置 LC 到当前地址,还初始化段寄存器的值为 0x0

jmp 指令

jmp $ 在这里的作用,是做一个无限循环,让 CPU 停在该指令处,不能再往下执行。

试想一下如果没有这个无限循环,CPU 就会按着 CS:IP 一路往下执行,能执行的则执行,不能执行的就 crash,我们不想让这样的事情发生。

jmp 跳转分 short jump,long jump,还分向前跳转,向后跳转。细看这个 jmp $ 指令,机器码是 eb fe,还有很多可以挖掘的地方,它是一个 short jump,是一个 reverse short jump,意思是向后跳转。具体操作是从 jmp $ 紧接着的下一个指令的地址开始算,往回跳转两个字节,因为 eb fe 就是两个字节,所以跳回指令本身,造成一个无限循环。

详细不展开,关于 jmp $ 指令,好文一篇。

times 指令

重复执行后面的操作 N 次。在 boot sector 程序中,最后两个字节固定,因此,填充 0 的操作应该进行 times 指令当前的地址,减去段起始地址的结果这么多次。

$ 操作符代表当前指令地址,$$ 代表段起始地址。

pusha popa 指令

调用方法的时候,如果方法不小心修改了寄存器的值,可能会造成意想不到的结果。我们不希望寄存器的值被修改,有一种办法是在调用方法之前,将所欲寄存器的值以及方法的返回地址都 push 到栈中,然后调用结束,再全部 pop 回来。

这样的操作很麻烦,所以 pusha 和 popa 指令,会帮助我们完成这一操作。pusha 在方法中调用一次,会将所有寄存器以及方法返回地址都保存到栈,popa 则执行相反的操作。

print_hex:pusha
...poparet

条件控制指令

看完这篇文章,一切都很清楚

跳转跳转跟在 CMP 指令之后使用,如:

cmp ax, 0x4
je jump_pointjump_point:do_something_here

最常用的是下面几个:

  • JE 如果相等,跳转
  • JNE 如果不相等,跳转
  • JG 如果大于目标,跳转
  • JGE 如果大于等于目标,跳转
  • JL 如果小于目标,跳转
  • JLE 如果小于等于目标,跳转

其他必要的汇编指令

最重要的部分已经讲完了,接下来,用示例代码的方式带过剩余简单的部分。

  • 字符串定义

最后的 0,添加一个 null byte 作为字符串结尾

HELLO:db 'Hello, World', 0GOODBYE:db 'Goodbye', 0
  • 文件包含

关于文件包含,有一个问题还没有解决,就是为什么文件包含要写在 jmp $ 指令之后。我试过将他们放在文件其他地方,确实会发生无法预料的结果。有待研究。

%include "boot_sect_print.asm"
%include "boot_sect_print_hex.asm"
  • 堆栈的使用

关于栈,记住几个点即可。

第一,后进先出 (LIFO);第二,BP 寄存器指向栈底,SP 寄存器指向栈顶;第三,栈从内存高位地址向低位地址增长;第四,push 一个值到栈,SP - 2;pop 一个值出栈,SP + 2

可以使用书中的代码进行理解。

mov ah, 0x0e ; int 10/ ah = 0eh -> scrolling teletype BIOS routine
mov bp, 0x8000 ; Set the base of the stack a little above where BIOS
mov sp, bp ; loads our boot sector - so it won ’t overwrite us.
push 'A' ; Push some characters on the stack for later
push 'B' ; retreival. Note , these are pushed on as
push 'C' ; 16 - bit values , so the most significant byte
; will be added by our assembler as 0 x00.
pop bx ; Note , we can only pop 16 - bits , so pop to bx
mov al, bl ; then copy bl ( i.e. 8- bit char ) to al
int 0x10 ; print (al)
pop bx ; Pop the next value
mov al, bl
int 0x10 ; print (al)
mov al , [0x7ffe ] ; To prove our stack grows downwards from bp ,
; fetch the char at 0 x8000 - 0x2 ( i.e. 16 - bits )
int 0x10 ; print (al)
jmp $ ; Jump forever.
; Padding and magic BIOS number.
times 510 - ($ - $$) db 0
dw 0xaa55

总结

  • Boot Sector 被 BIOS 加载到 0x7c00 的内存位置
  • 用程序证实了 0x7c00 物理内存位置上,确实是我们的 Boot Sector 程序
  • 寄存器的分类,寄存器的作用
  • 用段寄存器来完成寻找 Boot Sector 前两个字节内容的任务
  • 必要的汇编指令

下一篇,我们将讲解如何读取磁盘数据,之后就要开启我们的 32-bit

推荐阅读(参考链接):

  • https://wiki.osdev.org/Boot_Sequence
  • https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf
  • https://nets.ec/Ascii_shellcode
  • https://thestarman.pcministry.com/asm/debug/Segments.html
  • https://www.tutorialspoint.com/assembly_programming/assembly_registers.htm
  • http://dewkumar.blogspot.com/2012/01/what-is-org-origin-directive-in.html#:~:text=What%20is%20ORG%20(origin)%20directive%20in%20assembly%20level%20language%3F,expression%20in%20the%20operand%20field.
  • https://stackoverflow.com/questions/38318230/how-to-display-register-value-using-int-10h
  • https://en.wikipedia.org/wiki/INT_13H#INT_13h_AH.3D02h:_Read_Sectors_From_Drive
  • https://stackoverflow.com/questions/50260277/is-there-a-difference-between-org-0x7c00-and-mov-ax-07c0h
  • https://thestarman.pcministry.com/asm/2bytejumps.htm
  • http://ps-2.kev009.com/wisclibrary/aix52/usr/share/man/info/en_US/a_doc_lib/aixassem/alangref/absolute_add.htm
  • http://et.engr.iupui.edu/~skoskie/ECE362/lecture_notes/LN2_html/text8.html
  • https://en.m.wikipedia.org/wiki/X86_memory_segmentation
  • https://en.wikipedia.org/wiki/Segment_descriptor
  • http://ref.x86asm.net/coder32.html
  • https://www.tortall.net/projects/yasm/manual/html/objfmt-bin.html
  • http://www.sce.carleton.ca/courses/sysc-3006/s13/Lecture%20Notes/Part5-SimpleAssembly.pdf
  • https://stackoverflow.com/questions/4903906/assembly-using-the-data-segment-register-ds
  • https://wiki.osdev.org/Real_Mode
  • https://www.daniweb.com/programming/software-development/threads/291076/whats-org-100h#:~:text=ORG%20(abbr.,only%20one%20segment%20of%20max.
  • https://www.nasm.us/doc/nasmdoc3.html#section-3.5
  • https://www.tutorialspoint.com/assembly_programming/assembly_conditions.htm

0x120-从头开始写操作系统-启动扇区与内存的关系及内存寻址的应用相关推荐

  1. 自己动手写操作系统--个人实践

    近期開始看于渊的<自己动手写操作系统>这本书,刚開始看就发现做系统的引导盘居然是软盘!心里那个汗啊! 如今都是U盘了,谁还用软盘.于是考虑用U盘. 于是開始下面步骤: 1.既然书上说给先要 ...

  2. 若用MASM写操作系统的启动部分如何实现? 其他开发语言 / 汇编语言 - CSDN社区 community.csdn.net

    导读: 偶有个笨方法,不过总是可行的,偶也是成功了才几天       希望有参考价值     偶在windows98下写代码,工具DEBUG(windows自带),只有MASM5.0         ...

  3. 动手写操作系统系列-前言

    操作系统教程 操作系统教程 怎么使用scratch开发一个操作系统! 我常常想去学习怎么使用scratch开发一个操作系统.在大学,我学习了怎么实现高级特征(分页,信号量,内存管理等),但是: 我从来 ...

  4. 自己写操作系统学习总结

    怎样自己写一个简单的操作系统? https://www.zhihu.com/question/20207347 我写的时候一些经历: 第一次写的时候3000行左右的就无法调试了,当时主要参考了linu ...

  5. 动手写操作系统3----软盘读写逻辑实现

    系统从软盘启动,加载软盘第一个扇区作为引导扇区来加载操作系统,第一个扇区大小为512byte,一般用来跳转到操作系统代码起始处,第一个扇区称为引导区,现在来研究一下软盘的物理结构,引导扇区的数据格式以 ...

  6. 操作系统笔记(一)初识操作系统——启动过程

    操作系统 前言 什么是操作系统 揭盖钢琴的盖子 计算机上电后执行的第一条指令 对于x86PC 0x7c00处存放的代码 为什么这里用的汇编代码而不是用C写的呢? 操作系统启动步骤解读 操作系统启动 b ...

  7. 自己动手写操作系统系列第1篇,从开机加电到切换保护模式

    本系列为小组作业,参考了很多教程,包括ucore.30天自制操作系统.linux内核设计的艺术等内容.以及最重要的是小组里的几位大佬,本篇文章只是记录自己在学习过程中每一步的脚印,并包含了很多相关知识 ...

  8. 自己动手写操作系统之环境构建篇

    自己动手写操作系统之环境构建篇 最近开始看自己动手写操作系统,但是书中采用软盘启动很是郁闷,于是想是否可以从优盘启动呢?作为一名BIOS工程师,曾经用优盘启动过dos,linux等系统,于是做了如下尝 ...

  9. 「操作系统」《自己动手写操作系统》1.1前期准备工作

    一.软硬件 1.硬件 一台计算机(笔者使用的是Win7 x64操作系统) 2.软件 点击此处下载<自己动手写操作系统>的光盘配套文件以及相应工具 汇编编译器:NASM(资源文件中有) 软盘 ...

最新文章

  1. 只有你想不到,没有它做不到——可随时变身的模块化机器人
  2. 【漫谈数据仓库】 如何优雅地设计数据分层
  3. Hu 4639 hehe
  4. java useragent 360 遨游 火狐_各种浏览器UserAgent一览表(桌面+移动)
  5. vue点击ul中的li显示,点击其他地方隐藏
  6. Git的17条基本用法
  7. lgg8各个版本_LG正式推出G8SThinQ 搭载骁龙855
  8. 标准表达式中数据类型不匹配怎么解决_关于Inventor驱动尺寸中表达式的使用问题...
  9. OpenGL ES2 学习教程5——顶点属性
  10. web基础学习(十四)CSS3多列布局
  11. 大数据实战之路-数据仓库-项目运维交接文档
  12. db2的节点编目和数据库编目
  13. OpenGL 矩阵变换GLM库的使用
  14. python如何对excel批量加密_用python加密excel工作表
  15. [操作系统]进程同步 Reader-Writer问题 共享缓冲区问题 面包师问题 吸烟者问题
  16. matlab 2013至2016 32bit、64bit破解版集合 百度云盘下载
  17. 如何通过美股交易软件完成开户?美股开户交易要点有哪些?
  18. ORB_VI思想框架
  19. VS2022社区版安装教程
  20. C# 语言程序设计笔记

热门文章

  1. 北大青鸟ACCP一期云题库难题总结
  2. java强引用不会被回收_强引用(Strong Reference)-不回收
  3. web前端—前端三剑客之JS(12):字符串
  4. 电脑桌面云便签怎么绑定和开启微信提醒?
  5. Android Studio的报错提示:Error while Launching activity
  6. (14)写一个函数,将两个字符串连接
  7. 24基础指标、macd指标详解、macd指标分析
  8. hp打印机计算机接口,hp打印机的端口怎么设置?各是什么意思?
  9. iO逆向 触动精灵网络请求
  10. 一文说透企业风险管理的三大要素分别是什么