Orange‘s:一个操作系统的实现学习笔记3

前面几期文章

Orange‘s:一个操作系统的实现学习笔记1:https://blog.csdn.net/weixin_42193791/article/details/123144920
Orange‘s:一个操作系统的实现学习笔记2:
https://blog.csdn.net/weixin_42193791/article/details/123039173

文章目录

  • 前面几期文章
  • 一、前言
  • 二、代码实现了什么
  • 三、操作流程
    • 1.安装freedos
    • 2.编写代码
    • 3.编译
    • 4.拷贝
    • 5.运行测试
  • 一些问题


一、前言

书中上一小节实现了跳入保护模式,接下来本节将实验在保护模式下访问更高地址的内存,实现对更高地址内存的读写。

二、代码实现了什么

代码实现了一个段基址为5MB的段,先读出八个字节的内容,然后写入一个字符串,然后再一次读取用来确定是否写入成功。

三、操作流程

1.安装freedos

参考文章https://www.linuxidc.com/Linux/2014-05/102460.htm,打开链接https://bochs.sourceforge.io/diskimages.html下载freedos安装包,然后将其解压,得到一个一个文件夹,如下图所示:
Orange‘s:一个操作系统的实现学习笔记3_第1张图片
然后点击进入文件夹,如下图所示:
Orange‘s:一个操作系统的实现学习笔记3_第2张图片
之后先不要着急进行下一步操作,先来研究一下这里面的bochsrc和之前编写的bochsrc之间的区别,两个文件内容如下:

#之前编写的bochsrc
#Configuration file for Bochs

#how much memory the emulated machine will have
megs: 32

#filename of ROM images
romimage: file=/usr/local/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/local/share/bochs/VGABIOS-lgpl-latest

#what disk images will be used
floppya: 1_44=a.img, status=inserted

#choose the boot disk.
boot: floppy

#where do we send log messages
log: bochsout.txt

#disable the mouse
mouse: enabled=0

#enable key mapping, using US layout as default
keyboard:keymap=/usr/local/share/bochs/keymaps/x11-pc-us.map

#freedos自带的bochsrc
megs: 32
romimage: file=$BXSHARE/BIOS-bochs-latest
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
vga: extension=vbe, update_freq=15
floppya: 1_44=a.img, status=inserted
floppyb: 1_44=b.img, status=inserted
ata0-master: type=disk, path=c.img, cylinders=306, heads=4, spt=17
boot: c
log: bochsout.txt
mouse: enabled=0
cpu: ips=15000000

可以发现,megs和vgaromimage和romimage并没有变化,其他的变化参考这篇文章来分析:https://blog.csdn.net/yyttiao/article/details/8168301

floppyb: 1_44=b.img, status=inserted

新增一个软盘floppyb,然后status=inserted表示已经插入。

ata0-master: type=disk, path=c.img, cylinders=306, heads=4, spt=17

首先先来看ata是什么,参考文章:https://blog.csdn.net/xiaojianpitt/article/details/2288123
而上述代码则是ata0-master用于模拟系统中第一个ata通道上连接的第一个ata设备。

boot: c

boot指定虚拟系统启动引导的设备.由硬盘,软件,或者磁盘符号。
其他的变化参考上述文章一一分析就好了。

2.编写代码

编写pmtest2.asm

; ==========================================
; pmtest2.asm
; 编译方法:nasm pmtest2.asm -o pmtest2.com
; ==========================================

%include	"pm.inc"	; 常量,, 以及一些说明

org	0100h
jmp	LABEL_BEGIN
;程序跳转到LABEL_BEGIN执行

[SECTION .gdt]
; GDT
;                            段基址,        段界限 , 属性
LABEL_GDT:         Descriptor    0,              0, 0         
;空描述符
LABEL_DESC_NORMAL: Descriptor    0,         0ffffh, DA_DRW    
;Normal 描述符
LABEL_DESC_CODE32: Descriptor    0, SegCode32Len-1, DA_C+DA_32
;非一致代码段, 32位代码段
LABEL_DESC_CODE16: Descriptor    0,         0ffffh, DA_C      
;非一致代码段, 16位代码段,由此段跳入保护模式
LABEL_DESC_DATA:   Descriptor    0,      DataLen-1, DA_DRW    
;数据段,显示的字符串保存在数据段中
LABEL_DESC_STACK:  Descriptor    0,     TopOfStack, DA_DRWA+DA_32
;Stack, 堆栈段
LABEL_DESC_TEST:   Descriptor 0500000h,     0ffffh, DA_DRW
;测试段,在此段内实验访问大内存,段基址为5MB,远远超过实模式下地址上限1MB
LABEL_DESC_VIDEO:  Descriptor  0B8000h,     0ffffh, DA_DRW    
;显存首地址,显存段
;GDT 结束

GdtLen		equ	$ - LABEL_GDT	; GDT长度
GdtPtr		dw	GdtLen - 1	; GDT界限
		dd	0		; GDT基地址

; GDT 选择子
SelectorNormal		equ	LABEL_DESC_NORMAL	- LABEL_GDT
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorCode16		equ	LABEL_DESC_CODE16	- LABEL_GDT
SelectorData		equ	LABEL_DESC_DATA		- LABEL_GDT
SelectorStack		equ	LABEL_DESC_STACK	- LABEL_GDT
SelectorTest		equ	LABEL_DESC_TEST		- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT
; END of [SECTION .gdt]

;数据段
[SECTION .data1]	 
ALIGN	32
[BITS	32]
LABEL_DATA:
SPValueInRealMode	dw	0
; 字符串
PMMessage:		db	"In Protect Mode now. ^-^", 0	
;在保护模式显示的字符串1
OffsetPMMessage		equ	PMMessage - $$
;$$是当前段开始的地址
;OffsetPMMessage=PMMessage-$$为字符串相对于数据段开始地址的偏移地址
StrTest:		db	"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
;保护模式显示的字符串2
OffsetStrTest		equ	StrTest - $$
;OffsetStrTest=StrTest-$$为字符串相对于数据段开始地址的偏移地址
DataLen			equ	$ - LABEL_DATA
;DataLen为数据段长度
; END of [SECTION .data1]

;全局堆栈段
[SECTION .gs]
ALIGN	32
[BITS	32]
LABEL_STACK:
	times 512 db 0
	;重复定义512个字节的0

TopOfStack	equ	$ - LABEL_STACK - 1
;栈顶为当前代码行汇编后的地址-LABEL_STACK-1
; END of [SECTION .gs]

;16位代码段,用来跳入保护模式和跳回实模式之后的工作
[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, 0100h
	;初始化ds,es,ss,sp
	;注意,此时ds内部存储的值是cs的值
	;下面偏移地址是相对于初始化的cs的值

	mov	[LABEL_GO_BACK_TO_REAL+3], ax
	;实模式jmp指令结构是byte1:0EAh,byte2~3:Offset,byte4~5:Segment
	;此处mov将ax中的内容送到了LABEL_GO_BACK_TO_REAL后第四个字节和第五个字节
	;也就是jmp 0:LABEL_REAL_ENTRY变成了jmp ax:LABEL_REAL_ENTRY
	mov	[SPValueInRealMode], sp
	;将实模式下的指针sp存到SPValueInRealMode字符串中

	;初始化16位代码段描述符
	mov	ax, cs
	movzx	eax, ax
	;movzx,movsx要求操作数B所占空间小于操作数A
	;movzx将空位补充为0,movsx将空位补充为操作数B的符号位
	shl	eax, 4
	add	eax, LABEL_SEG_CODE16
	mov	word [LABEL_DESC_CODE16 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE16 + 4], al
	mov	byte [LABEL_DESC_CODE16 + 7], ah
	;和上一节一样,计算真实物理地址后送入段描述符

	;初始化32位代码段描述符
	xor	eax, eax
	;xor异或指令,两个寄存器对应位相同为0,不同为1
	;eax肯定与eax内部数据相同,所以xor eax,eax后eax每一位都为0,实现eax清零
	mov	ax, cs
	shl	eax, 4
	add	eax, LABEL_SEG_CODE32
	mov	word [LABEL_DESC_CODE32 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE32 + 4], al
	mov	byte [LABEL_DESC_CODE32 + 7], ah
	;和上一节一样,计算真实物理地址后送入段描述符
	;注意,上述两个代码段(16位代码段和32位代码段)的段基址就是cs中的地址

	; 初始化数据段描述符
	xor	eax, eax
	mov	ax, ds
	;这里有一个很有意思的事情,如果简单来看,ds寄存器保存着数据段的段基址
	;那么直接将ds中的内容送到ax中,之后左移4位加偏移是在初始化数据段的描述符
	;然而仔细观察,会发现这里ds还是最初cs的值,而且开头很多段的段基址都是0
	;原因是数据段包含了段基址是0的段,数据段里面字符串地址都是加上偏移地址
	shl	eax, 4
	add	eax, LABEL_DATA
	mov	word [LABEL_DESC_DATA + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_DATA + 4], al
	mov	byte [LABEL_DESC_DATA + 7], ah
	;和上一节一样,计算真实物理地址后送入段描述符

	; 初始化堆栈段描述符
	xor	eax, eax
	mov	ax, ds
	;堆栈段和数据段的段基址是相同的,所以直接将ds寄存器中的内容送到ax
	shl	eax, 4
	add	eax, LABEL_STACK
	mov	word [LABEL_DESC_STACK + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_STACK + 4], al
	mov	byte [LABEL_DESC_STACK + 7], ah
	;和上一节一样,计算真实物理地址后送入段描述符

	; 为加载 GDTR 作准备
	xor	eax, eax
	mov	ax, ds
	;这里gdt的基址和ds都是cs的最开始的值,所以直接将ds寄存器中的内容送到ax
	shl	eax, 4
	add	eax, LABEL_GDT		; eax <- gdt 基地址
	mov	dword [GdtPtr + 2], eax	; [GdtPtr + 2] <- gdt 基地址

	; 加载 GDTR
	lgdt	[GdtPtr]

	; 关中断
	cli

	; 打开地址线A20
	in	al, 92h
	or	al, 00000010b
	out	92h, al

	; 准备切换到保护模式
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax

	; 真正进入保护模式
	jmp	dword SelectorCode32:0	
	;道理同上述的jmp指令结构
	;执行这一句会把 SelectorCode32装入 cs, 并跳转到 Code32Selector:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:		; 从保护模式跳回到实模式就到了这里
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	;将ds、es、ss设置成实模式下的地址

	mov	sp, [SPValueInRealMode]
	;sp(堆栈指针)设置成实模式下的地址

	in	al, 92h		; `.
	and	al, 11111101b	;  | 关闭 A20 地址线
	out	92h, al		; /

	sti			; 开中断

	mov	ax, 4c00h	; `.
	;4c00h=0100 1100 0000 0000b
	;21h中断的功能由ah寄存器有关
	;ah=4c时,21h的功能是代返回码结束,返回码是al中的内容
	int	21h		; /  回到 DOS
; END of [SECTION .s16]

;32位代码段
[SECTION .s32]
[BITS	32]

LABEL_SEG_CODE32:
	mov	ax, SelectorData
	mov	ds, ax			; 数据段选择子
	mov	ax, SelectorTest
	mov	es, ax			; 测试段选择子
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子
	;注意,保护模式下,对应的段寄存器存储的都是对应段的索引
	;段选择子中保存着段的索引

	mov	ax, SelectorStack
	mov	ss, ax			; 堆栈段选择子

	mov	esp, TopOfStack
	;保护模式的栈指针存储在esp寄存器中
	;esp寄存器是sp寄存器的32位版,esp的低16位就是sp寄存器


	; 下面显示一个字符串
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	xor	esi, esi
	xor	edi, edi
	;清零esi,edi
	mov	esi, OffsetPMMessage	
	;源数据偏移
	;数据段中PMMessage字符串的首地址送到esi
	mov	edi, (80 * 10 + 0) * 2	
	; 目的数据偏移。屏幕第 10,0 列。
	cld
	;cld是将标志寄存器的DF(方向标志位)置零
	;标志寄存器中的DF位,串处理指令指令中,每次操作后,控制si、di是递减或递增
	;DF=0,si、di递增,DF=1,si、di递减
.1:
	lodsb
	;lodsb/lodsw指令是块装入指令,将si指向的存储单元的数据读入累加器
	;lodsb是将数据读到al,lodsw是将数据读到ax
	;读取完成后,si将自动增加或减少12
	;常用作处理字符串或数组中的元素逐个处理
	test	al, al
	;test指令是将两个操作数进行逻辑与运算,但是不会改变寄存器内的值
	;test指令操作完成后,寄存器中的内容没有改变,指令根据操作结果设置标志位
	;test指令常用于判断寄存器内容是否为空
	;如果寄存器为空,则逻辑与运算为0,ZF位被置零
	;如果ZF位为0,则jz跳转
	jz	.2
	;如果字符串读取结束,则跳转到.2,若未结束则继续进行下列指令
	mov	[gs:edi], ax
	;将ax中的内容送到gs:edi所形成的地址指向的位置
	add	edi, 2
	;esi+2,一次读取两个存储单元(2bit)
	jmp	.1
	;字符串未读取完跳转到.1循环读取
.2:	; 显示完毕

	call	DispReturn
	;调用DIsReturn

	call	TestRead
	;先读取一次地址对应的存储单元中的数据并显示
	call	TestWrite
	;然后将字符串写入地址对应的存储单元
	call	TestRead
	;再读取一次地址对应的存储单元中的数据并显示

	; 到此停止
	jmp	SelectorCode16:0
	;返回实模式

; ------------------------------------------------------------------------
TestRead:
	xor	esi, esi
	;清零esi
	mov	ecx, 8
	;将ecx赋值为8
	;读取八个bit的数据
.loop:
	mov	al, [es:esi]
	;将es:esi形成的地址中的数据送到al
	call	DispAL
	;调用DispAL
	inc	esi
	;esi+1
	loop	.loop
	;循环8次

	call	DispReturn
	;调用DispReturn

	ret
; TestRead 结束-----------------------------------------------------------


; ------------------------------------------------------------------------
TestWrite:
	push	esi
	push	edi
	;压栈esi、edi
	xor	esi, esi
	xor	edi, edi
	;清零esi,edi
	mov	esi, OffsetStrTest	; 源数据偏移
	;将StrTest字符串的偏移地址送到esi
	cld
.1:
	lodsb
	test	al, al
	jz	.2
	mov	[es:edi], al
	;读取时候显示地址是gs:edi,这里是es:edi,是将al中的数据送到es:edi地址
	inc	edi
	;edi+1
	jmp	.1
.2:

	pop	edi
	pop	esi
	;读取结束后将edi和esi出栈

	ret
; TestWrite 结束----------------------------------------------------------


; ------------------------------------------------------------------------
; 显示 AL 中的数字
; 默认地:
;	数字已经存在 AL 中
;	edi 始终指向要显示的下一个字符的位置
; 被改变的寄存器:
;	ax, edi
; ------------------------------------------------------------------------
DispAL:
	push	ecx
	push	edx

	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	dl, al
	shr	al, 4
	mov	ecx, 2
.begin:
	and	al, 01111b
	cmp	al, 9
	ja	.1
	add	al, '0'
	jmp	.2
.1:
	sub	al, 0Ah
	add	al, 'A'
.2:
	mov	[gs:edi], ax
	add	edi, 2

	mov	al, dl
	loop	.begin
	add	edi, 2

	pop	edx
	pop	ecx

	ret
; DispAL 结束-------------------------------------------------------------


; ------------------------------------------------------------------------
DispReturn:
	push	eax
	push	ebx
	;压栈eax,ebx
	mov	eax, edi
	;将edi送到eax
	mov	bl, 160
	;bl赋值160
	div	bl
	;eax除以bl
	and	eax, 0FFh
	;eax与0FFh相与
	inc	eax
	;eax加1
	mov	bl, 160
	mul	bl
	;eax乘bl
	mov	edi, eax
	;将eax中的内容送到edi
	pop	ebx
	pop	eax
	;出栈ebx、eax
	;作用大概是回车

	ret
; DispReturn 结束---------------------------------------------------------

SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]


;16 位代码段.32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN	32
[BITS	16]
LABEL_SEG_CODE16:
	;跳回实模式:
	mov	ax, SelectorNormal
	mov	ds, ax
	mov	es, ax
	mov	fs, ax
	mov	gs, ax
	mov	ss, ax
	;为了将寄存器设置为实模式下的值,这里向ax送入normal段的选择子

	mov	eax, cr0
	and	al, 11111110b
	mov	cr0, eax
	;PE位置零,跳回实模式

LABEL_GO_BACK_TO_REAL:
	jmp	0:LABEL_REAL_ENTRY	
	;段地址会在程序开始处被设置成正确的值
	;此处参照之前jmp指令结构

Code16Len	equ	$ - LABEL_SEG_CODE16

; END of [SECTION .s16code]


编写pm.inc:


;pm.inc
; 描述符图示

; 图示一
;
;  ------ ┏━━┳━━┓高地址
;7  ┃ 段 ┃
;         ┣━━┫    ┃
;;  字节 7 ┆    ┆    ┆
;;         ┣━━┫ ② ┃
;0  ┃    ┃
;  ------ ┣━━╋━━┫
;7  ┃ G  ┃
;         ┣━━╉──┨
;6  ┃ D  ┃
;         ┣━━╉──┨
;50;         ┣━━╉──┨
;4  ┃ AVL┃
;  字节 6 ┣━━╉──┨
;3  ┃    ┃
;         ┣━━┫ 段 ┃
;2  ┃ 界 ┃
;         ┣━━┫ 限 ┃
;1  ┃    ┃
;         ┣━━┫ ② ┃
;0  ┃    ┃
;  ------ ┣━━╋━━┫
;7  ┃ P  ┃
;         ┣━━╉──┨
;6  ┃    ┃
;         ┣━━┫ DPL┃
;5  ┃    ┃
;         ┣━━╉──┨
;4  ┃ S  ┃
;  字节 5 ┣━━╉──┨
;3  ┃    ┃
;         ┣━━┫ T  ┃
;2  ┃ Y  ┃
;         ┣━━┫ P  ┃
;1  ┃ E  ┃
;         ┣━━┫    ┃
;0  ┃    ┃
;  ------ ┣━━╋━━┫
;23 ┃    ┃
;         ┣━━┫    ┃
;22 ┃    ┃
;         ┣━━┫ 段 ┃
;
;   字节  ┆    ┆ 基 ┆
; 2, 3, 4
;         ┣━━┫ 址 ┃
;1  ┃ ① ┃
;         ┣━━┫    ┃
;0  ┃    ┃
;  ------ ┣━━╋━━┫
;15 ┃    ┃
;         ┣━━┫    ┃
;14 ┃    ┃
;         ┣━━┫ 段 ┃
;
; 字节 0,1┆    ┆ 界 ┆
;
;         ┣━━┫ 限 ┃
;1  ┃ ① ┃
;         ┣━━┫    ┃
;0  ┃    ┃
;  ------ ┗━━┻━━┛低地址
;


; 图示二

; 高地址………………………………………………………………………低地址

; |   7   |   6   |   5   |   4   |   3   |   2   |   1   |   0    |
; |7654321076543210765432107654321076543210765432107654321076543210|	<-8 字节
; |--------========--------========--------========--------========|
; ┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓
;31..24(见下图)   ┃     段基址(23..0)    ┃ 段界限(15..0); ┃      ┃              ┃                      ┃              ┃
; ┃ 基址2┃③│②│    ①┃基址1b│   基址1a     ┃    段界限1; ┣━━━╋━━━┳━━━╋━━━━━━━━━━━╋━━━━━━━┫
;%6%5%4%3%2%1; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━┛
;         │                \_________
;         │                          \__________________
;         │                                             \________________________________________________
;         │                                                                                              \
;         ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
;7654321076543210;         ┣━━╋━━╋━━╋━━╋━━┻━━┻━━┻━━╋━━╋━━┻━━╋━━╋━━┻━━┻━━┻━━┫
;         ┃ G  ┃D/B ┃ 0  ┃ AVL┃   段界限 2 (19..16)  ┃  P ┃   DPL    ┃ S  ┃       TYPE           ┃
;         ┣━━┻━━┻━━┻━━╋━━━━━━━━━━━╋━━┻━━━━━┻━━┻━━━━━━━━━━━┫
;         ┃      ③: 属性 2      ┃    ②: 段界限 2      ┃                   ①: 属性1;         ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┛
;       高地址                                                                                          低地址
;
;

; 说明:
;
; (1) P:    存在(Present)位。
;		P=1 表示描述符对地址转换是有效的,或者说该描述符所描述的段存在,即在内存中;
;		P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常。
;
; (2) DPL:  表示描述符特权级(Descriptor Privilege level),共2位。它规定了所描述段的特权级,用于特权检查,以决定对该段能否访问。 
;
; (3) S:   说明描述符的类型。
;		对于存储段描述符而言,S=1,以区别与系统段描述符和门描述符(S=0);
; (4) TYPE: 说明存储段描述符所描述的存储段的具体属性。
;
;		 
;	数据段类型	类型值		说明
;			----------------------------------
;			0		只读 
;			1		只读、已访问 
;			2/;			3/写、已访问 
;			4		只读、向下扩展 
;			5		只读、向下扩展、已访问 
;			6/写、向下扩展 
;			7/写、向下扩展、已访问 
;
;		
;			类型值		说明
;	代码段类型	----------------------------------
;			8		只执行 
;			9		只执行、已访问 
;			A		执行/;			B		执行/读、已访问 
;			C		只执行、一致码段 
;			D		只执行、一致码段、已访问 
;			E		执行/读、一致码段 
;			F		执行/读、一致码段、已访问 
;
;		
;	系统段类型	类型编码	说明
;			----------------------------------
;			0		<未定义>
;			1		可用286TSS
;			2		LDT
;			3		忙的286TSS
;			4		286调用门
;			5		任务门
;			6		286中断门
;			7		286陷阱门
;			8		未定义
;			9		可用386TSS
;			A		<未定义>
;			B		忙的386TSS
;			C		386调用门
;			D		<未定义>
;			E		386中断门
;			F		386陷阱门
;
; (5) G:    段界限粒度(Granularity)位。
;		G=0 表示界限粒度为字节;
;		G=1 表示界限粒度为4K 字节。
;           注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。 
;
; (6) D/B:  此位是一个很特殊的位,在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同。 
;           ⑴ 在描述可执行段的描述符中,D位决定了指令使用的地址及操作数所默认的大小。
;		① D=1表示默认情况下指令使用32位地址及32位或8位操作数,这样的代码段也称为32位代码段;
;		② D=0 表示默认情况下,使用16位地址及16位或8位操作数,这样的代码段也称为16位代码段,它与80286兼容。可以使用地址大小前缀和操作数大小前缀分别改变默认的地址或操作数的大小。 
;           ⑵ 在向下扩展数据段的描述符中,D位决定段的上部边界。
;		① D=1表示段的上部界限为4G;
;		② D=0表示段的上部界限为64K,这是为了与80286兼容。 
;           ⑶ 在描述由SS寄存器寻址的段描述符中,D位决定隐式的堆栈访问指令(如PUSH和POP指令)使用何种堆栈指针寄存器。
;		① D=1表示使用32位堆栈指针寄存器ESP;
;		② D=0表示使用16位堆栈指针寄存器SP,这与80286兼容。 
;
; (7) AVL:  软件可利用位。80386对该位的使用未左规定,Intel公司也保证今后开发生产的处理器只要与80386兼容,就不会对该位的使用做任何定义或规定。 
;


;----------------------------------------------------------------------------
; 描述符类型值说明
; 其中:
;       DA_  : Descriptor Attribute
;       D    : 数据段
;       C    : 代码段
;       S    : 系统段
;       R    : 只读
;       RW   : 读写
;       A    : 已访问
;       其它 : 可按照字面意思理解
;----------------------------------------------------------------------------
DA_32		EQU	4000h	; 32 位段

DA_DPL0		EQU	  00h	; DPL = 0
DA_DPL1		EQU	  20h	; DPL = 1
DA_DPL2		EQU	  40h	; DPL = 2
DA_DPL3		EQU	  60h	; DPL = 3
;----------------------------------------------------------------------------
; 存储段描述符类型值说明
;----------------------------------------------------------------------------
DA_DR		EQU	90h	; 存在的只读数据段类型值
DA_DRW		EQU	92h	; 存在的可读写数据段属性值
DA_DRWA		EQU	93h	; 存在的已访问可读写数据段类型值
DA_C		EQU	98h	; 存在的只执行代码段属性值
DA_CR		EQU	9Ah	; 存在的可执行可读代码段属性值
DA_CCO		EQU	9Ch	; 存在的只执行一致代码段属性值
DA_CCOR		EQU	9Eh	; 存在的可执行可读一致代码段属性值
;----------------------------------------------------------------------------
; 系统段描述符类型值说明
;----------------------------------------------------------------------------
DA_LDT		EQU	  82h	; 局部描述符表段类型值
DA_TaskGate	EQU	  85h	; 任务门类型值
DA_386TSS	EQU	  89h	; 可用 386 任务状态段类型值
DA_386CGate	EQU	  8Ch	; 386 调用门类型值
DA_386IGate	EQU	  8Eh	; 386 中断门类型值
DA_386TGate	EQU	  8Fh	; 386 陷阱门类型值
;----------------------------------------------------------------------------


; 选择子图示:
;         ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
;1514131211109876543210;         ┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫
;         ┃                                 描述符索引                                 ┃ TI ┃   RPL    ┃
;         ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛
;
; RPL(Requested Privilege Level): 请求特权级,用于特权检查。
;
; TI(Table Indicator): 引用描述符表指示位
;	TI=0 指示从全局描述符表GDT中读取描述符;
;	TI=1 指示从局部描述符表LDT中读取描述符。
;

;----------------------------------------------------------------------------
; 选择子类型值说明
; 其中:
;       SA_  : Selector Attribute

SA_RPL0		EQU	0	; ┓
SA_RPL1		EQU	1	; ┣ RPL
SA_RPL2		EQU	2	; ┃
SA_RPL3		EQU	3	; ┛

SA_TIG		EQU	0	; ┓TI
SA_TIL		EQU	4	;;----------------------------------------------------------------------------





;------------------------------------------------------------------------------------------------------
;
; 描述符
; usage: Descriptor Base, Limit, Attr
;        Base:  dd
;        Limit: dd (low 20 bits available)
;        Attr:  dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
	dw	%2 & 0FFFFh				; 段界限 1				(2 字节)
	dw	%1 & 0FFFFh				; 段基址 1				(2 字节)
	db	(%1 >> 16) & 0FFh			; 段基址 2				(1 字节)
	dw	((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)	; 属性 1 + 段界限 2 + 属性 2		(2 字节)
	db	(%1 >> 24) & 0FFh			; 段基址 3				(1 字节)
%endmacro ;8 字节
;
;; usage: Gate Selector, Offset, DCount, Attr
;        Selector:  dw
;        Offset:    dd
;        DCount:    db
;        Attr:      db
%macro Gate 4
	dw	(%2 & 0FFFFh)				; 偏移 1				(2 字节)
	dw	%1					; 选择子				(2 字节)
	dw	(%3 & 1Fh) | ((%4 << 8) & 0FF00h)	; 属性					(2 字节)
	dw	((%2 >> 16) & 0FFFFh)			; 偏移 2				(2 字节)
%endmacro ;8 字节
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

上述代码大概写了算比较详细的注释。

3.编译

将代码编译,在文件夹中打开终端,输入:

nasm pmtest2.asm -o pmtest2.com

如下图所示:
Orange‘s:一个操作系统的实现学习笔记3_第3张图片

4.拷贝

首先用bximage虚拟软盘,叫做pm.img。
然后将pm.img放到freedos文件夹下,如下图所示:
Orange‘s:一个操作系统的实现学习笔记3_第4张图片
然后修改bochsrc,如下面代码所示:

megs: 32
romimage: file=$BXSHARE/BIOS-bochs-latest
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
vga: extension=vbe, update_freq=15
floppya: 1_44=a.img, status=inserted
floppyb: 1_44=pm.img, status=inserted
ata0-master: type=disk, path=c.img, cylinders=306, heads=4, spt=17
boot: a
log: bochsout.txt
mouse: enabled=0
cpu: ips=15000000

然后在文件夹下启动终端,输入:

bochs

然后进入freedos,如下图所示:
Orange‘s:一个操作系统的实现学习笔记3_第5张图片
然后再输入:

format b:

如下图所示:
Orange‘s:一个操作系统的实现学习笔记3_第6张图片
然后关闭bochs,然后在Ubuntu的freedos文件夹下打开终端,输入下述指令:

sudo mount -o loop pm.inc
sudo cp pmtest2.com /mnt/floppy
sudo umonut /mnt/floppy

Orange‘s:一个操作系统的实现学习笔记3_第7张图片
上述操作是将被格式化的pm.img挂载到Ubuntu上,然后将pmtest2.com拷贝到pm.inc。

5.运行测试

启动bochs,输入:

bochs

在freedos中输入:

b:\pmtest2.com

Orange‘s:一个操作系统的实现学习笔记3_第8张图片
可以看到上图已经显示出程序写入的字符串了。(A的asc码转为十六进制为41)

一些问题

还有一些细节问题需要记录,今天稍微有点累(打游戏上分了,我没有摆烂,扶我起来,我还能卷),先挂在这,明天或者后天再更。

你可能感兴趣的:(学习,bash,linux)