Browse Source

init

master
abbycin 1 year ago
commit
a4ac56d5cf
31 changed files with 9574 additions and 0 deletions
  1. 7
    0
      .gitignore
  2. 103
    0
      c05/c05_mbr.asm
  3. 103
    0
      c05/c05_mbr.lst
  4. 56
    0
      c06/c06_mbr.asm
  5. 68
    0
      c07/c07_mbr.asm
  6. 209
    0
      c08/c08.asm
  7. 247
    0
      c08/c08.lst
  8. 154
    0
      c08/c08_mbr.asm
  9. 154
    0
      c08/c08_mbr.lst
  10. 304
    0
      c09/c09_1.asm
  11. 71
    0
      c09/c09_2.asm
  12. 99
    0
      c11/c11_mbr.asm
  13. 111
    0
      c12/c12_mbr.asm
  14. 87
    0
      c13/c13.asm
  15. 95
    0
      c13/c13.lst
  16. 601
    0
      c13/c13_core.asm
  17. 221
    0
      c13/c13_mbr.asm
  18. 222
    0
      c13/c13_mbr.lst
  19. 2
    0
      c13/diskdata.txt
  20. 880
    0
      c14/c14_core.asm
  21. 911
    0
      c14/c14_core.lst
  22. 81
    0
      c15/c15.asm
  23. 976
    0
      c15/c15_core.asm
  24. 64
    0
      c16/c16.asm
  25. 1084
    0
      c16/c16_core.asm
  26. 46
    0
      c17/c17-1.asm
  27. 46
    0
      c17/c17-2.asm
  28. 1081
    0
      c17/c17_core.asm
  29. 203
    0
      c17/c17_mbr.asm
  30. 1085
    0
      c18/c18_core.asm
  31. 203
    0
      c18/c18_mbr.asm

+ 7
- 0
.gitignore View File

@@ -0,0 +1,7 @@
*.o
*.bin
*.ini
*.exe
*.vhd
*.rar
*.xml

+ 103
- 0
c05/c05_mbr.asm View File

@@ -0,0 +1,103 @@
;代码清单5-1
;文件名:c05_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-3-31 21:15
mov ax,0xb800 ;指向文本模式的显示缓冲区
mov es,ax

;以下显示字符串"Label offset:"
mov byte [es:0x00],'L'
mov byte [es:0x01],0x07
mov byte [es:0x02],'a'
mov byte [es:0x03],0x07
mov byte [es:0x04],'b'
mov byte [es:0x05],0x07
mov byte [es:0x06],'e'
mov byte [es:0x07],0x07
mov byte [es:0x08],'l'
mov byte [es:0x09],0x07
mov byte [es:0x0a],' '
mov byte [es:0x0b],0x07
mov byte [es:0x0c],"o"
mov byte [es:0x0d],0x07
mov byte [es:0x0e],'f'
mov byte [es:0x0f],0x07
mov byte [es:0x10],'f'
mov byte [es:0x11],0x07
mov byte [es:0x12],'s'
mov byte [es:0x13],0x07
mov byte [es:0x14],'e'
mov byte [es:0x15],0x07
mov byte [es:0x16],'t'
mov byte [es:0x17],0x07
mov byte [es:0x18],':'
mov byte [es:0x19],0x07

mov ax,number ;取得标号number的偏移地址
mov bx,10

;设置数据段的基地址
mov cx,cs
mov ds,cx

;求个位上的数字
mov dx,0
div bx
mov [0x7c00+number+0x00],dl ;保存个位上的数字

;求十位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x01],dl ;保存十位上的数字

;求百位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x02],dl ;保存百位上的数字

;求千位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x03],dl ;保存千位上的数字

;求万位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x04],dl ;保存万位上的数字

;以下用十进制显示标号的偏移地址
mov al,[0x7c00+number+0x04]
add al,0x30
mov [es:0x1a],al
mov byte [es:0x1b],0x04
mov al,[0x7c00+number+0x03]
add al,0x30
mov [es:0x1c],al
mov byte [es:0x1d],0x04
mov al,[0x7c00+number+0x02]
add al,0x30
mov [es:0x1e],al
mov byte [es:0x1f],0x04

mov al,[0x7c00+number+0x01]
add al,0x30
mov [es:0x20],al
mov byte [es:0x21],0x04

mov al,[0x7c00+number+0x00]
add al,0x30
mov [es:0x22],al
mov byte [es:0x23],0x04
mov byte [es:0x24],'D'
mov byte [es:0x25],0x07
infi: jmp near infi ;无限循环
number db 0,0,0,0,0
times 203 db 0
db 0x55,0xaa

+ 103
- 0
c05/c05_mbr.lst View File

@@ -0,0 +1,103 @@
1 ;代码清单5-1
2 ;文件名:c05_mbr.asm
3 ;文件说明:硬盘主引导扇区代码
4 ;创建日期:2011-3-31 21:15
5
6 00000000 B800B8 mov ax,0xb800 ;指向文本模式的显示缓冲区
7 00000003 8EC0 mov es,ax
8
9 ;以下显示字符串"Label offset:"
10 00000005 26C60600004C mov byte [es:0x00],'L'
11 0000000B 26C606010007 mov byte [es:0x01],0x07
12 00000011 26C606020061 mov byte [es:0x02],'a'
13 00000017 26C606030007 mov byte [es:0x03],0x07
14 0000001D 26C606040062 mov byte [es:0x04],'b'
15 00000023 26C606050007 mov byte [es:0x05],0x07
16 00000029 26C606060065 mov byte [es:0x06],'e'
17 0000002F 26C606070007 mov byte [es:0x07],0x07
18 00000035 26C60608006C mov byte [es:0x08],'l'
19 0000003B 26C606090007 mov byte [es:0x09],0x07
20 00000041 26C6060A0020 mov byte [es:0x0a],' '
21 00000047 26C6060B0007 mov byte [es:0x0b],0x07
22 0000004D 26C6060C006F mov byte [es:0x0c],"o"
23 00000053 26C6060D0007 mov byte [es:0x0d],0x07
24 00000059 26C6060E0066 mov byte [es:0x0e],'f'
25 0000005F 26C6060F0007 mov byte [es:0x0f],0x07
26 00000065 26C606100066 mov byte [es:0x10],'f'
27 0000006B 26C606110007 mov byte [es:0x11],0x07
28 00000071 26C606120073 mov byte [es:0x12],'s'
29 00000077 26C606130007 mov byte [es:0x13],0x07
30 0000007D 26C606140065 mov byte [es:0x14],'e'
31 00000083 26C606150007 mov byte [es:0x15],0x07
32 00000089 26C606160074 mov byte [es:0x16],'t'
33 0000008F 26C606170007 mov byte [es:0x17],0x07
34 00000095 26C60618003A mov byte [es:0x18],':'
35 0000009B 26C606190007 mov byte [es:0x19],0x07
36
37 000000A1 B8[2E01] mov ax,number ;取得标号number的偏移地址
38 000000A4 BB0A00 mov bx,10
39
40 ;设置数据段的基地址
41 000000A7 8CC9 mov cx,cs
42 000000A9 8ED9 mov ds,cx
43
44 ;求个位上的数字
45 000000AB BA0000 mov dx,0
46 000000AE F7F3 div bx
47 000000B0 8816[2E7D] mov [0x7c00+number+0x00],dl ;保存个位上的数字
48
49 ;求十位上的数字
50 000000B4 31D2 xor dx,dx
51 000000B6 F7F3 div bx
52 000000B8 8816[2F7D] mov [0x7c00+number+0x01],dl ;保存十位上的数字
53
54 ;求百位上的数字
55 000000BC 31D2 xor dx,dx
56 000000BE F7F3 div bx
57 000000C0 8816[307D] mov [0x7c00+number+0x02],dl ;保存百位上的数字
58
59 ;求千位上的数字
60 000000C4 31D2 xor dx,dx
61 000000C6 F7F3 div bx
62 000000C8 8816[317D] mov [0x7c00+number+0x03],dl ;保存千位上的数字
63
64 ;求万位上的数字
65 000000CC 31D2 xor dx,dx
66 000000CE F7F3 div bx
67 000000D0 8816[327D] mov [0x7c00+number+0x04],dl ;保存万位上的数字
68
69 ;以下用十进制显示标号的偏移地址
70 000000D4 A0[327D] mov al,[0x7c00+number+0x04]
71 000000D7 0430 add al,0x30
72 000000D9 26A21A00 mov [es:0x1a],al
73 000000DD 26C6061B0004 mov byte [es:0x1b],0x04
74
75 000000E3 A0[317D] mov al,[0x7c00+number+0x03]
76 000000E6 0430 add al,0x30
77 000000E8 26A21C00 mov [es:0x1c],al
78 000000EC 26C6061D0004 mov byte [es:0x1d],0x04
79
80 000000F2 A0[307D] mov al,[0x7c00+number+0x02]
81 000000F5 0430 add al,0x30
82 000000F7 26A21E00 mov [es:0x1e],al
83 000000FB 26C6061F0004 mov byte [es:0x1f],0x04
84
85 00000101 A0[2F7D] mov al,[0x7c00+number+0x01]
86 00000104 0430 add al,0x30
87 00000106 26A22000 mov [es:0x20],al
88 0000010A 26C606210004 mov byte [es:0x21],0x04
89
90 00000110 A0[2E7D] mov al,[0x7c00+number+0x00]
91 00000113 0430 add al,0x30
92 00000115 26A22200 mov [es:0x22],al
93 00000119 26C606230004 mov byte [es:0x23],0x04
94
95 0000011F 26C606240044 mov byte [es:0x24],'D'
96 00000125 26C606250007 mov byte [es:0x25],0x07
97
98 0000012B E9FDFF infi: jmp near infi ;无限循环
99
100 0000012E 0000000000 number db 0,0,0,0,0
101
102 00000133 00<rept> times 203 db 0
103 000001FE 55AA db 0x55,0xaa

+ 56
- 0
c06/c06_mbr.asm View File

@@ -0,0 +1,56 @@
;代码清单6-1
;文件名:c06_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-4-12 22:12
jmp near start
mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\
'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07
number db 0,0,0,0,0
start:
mov ax,0x7c0 ;设置数据段基地址
mov ds,ax
mov ax,0xb800 ;设置附加段基地址
mov es,ax
cld
mov si,mytext
mov di,0
mov cx,(number-mytext)/2 ;实际上等于 13
rep movsw
;得到标号所代表的偏移地址
mov ax,number
;计算各个数位
mov bx,ax
mov cx,5 ;循环次数
mov si,10 ;除数
digit:
xor dx,dx
div si
mov [bx],dl ;保存数位
inc bx
loop digit
;显示各个数位
mov bx,number
mov si,4
show:
mov al,[bx+si]
add al,0x30
mov ah,0x04
mov [es:di],ax
add di,2
dec si
jns show
mov word [es:di],0x0744

jmp near $

times 510-($-$$) db 0
db 0x55,0xaa

+ 68
- 0
c07/c07_mbr.asm View File

@@ -0,0 +1,68 @@
;代码清单7-1
;文件名:c07_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-4-13 18:02
jmp near start
message db '1+2+3+...+100='
start:
mov ax,0x7c0 ;设置数据段的段基地址
mov ds,ax

mov ax,0xb800 ;设置附加段基址到显示缓冲区
mov es,ax

;以下显示字符串
mov si,message
mov di,0
mov cx,start-message
@g:
mov al,[si]
mov [es:di],al
inc di
mov byte [es:di],0x07
inc di
inc si
loop @g

;以下计算1到100的和
xor ax,ax
mov cx,1
@f:
add ax,cx
inc cx
cmp cx,100
jle @f

;以下计算累加和的每个数位
xor cx,cx ;设置堆栈段的段基地址
mov ss,cx
mov sp,cx

mov bx,10
xor cx,cx
@d:
inc cx
xor dx,dx
div bx
or dl,0x30
push dx
cmp ax,0
jne @d

;以下显示各个数位
@a:
pop dx
mov [es:di],dl
inc di
mov byte [es:di],0x07
inc di
loop @a
jmp near $

times 510-($-$$) db 0
db 0x55,0xaa

+ 209
- 0
c08/c08.asm View File

@@ -0,0 +1,209 @@
;代码清单8-2
;文件名:c08.asm
;文件说明:用户程序
;创建日期:2011-5-5 18:17
;===============================================================================
SECTION header vstart=0 ;定义用户程序头部段
program_length dd program_end ;程序总长度[0x00]
;用户程序入口点
code_entry dw start ;偏移地址[0x04]
dd section.code_1.start ;段地址[0x06]
realloc_tbl_len dw (header_end-code_1_segment)/4
;段重定位表项个数[0x0a]
;段重定位表
code_1_segment dd section.code_1.start ;[0x0c]
code_2_segment dd section.code_2.start ;[0x10]
data_1_segment dd section.data_1.start ;[0x14]
data_2_segment dd section.data_2.start ;[0x18]
stack_segment dd section.stack.start ;[0x1c]
header_end:
;===============================================================================
SECTION code_1 align=16 vstart=0 ;定义代码段1(16字节对齐)
put_string: ;显示串(0结尾)。
;输入:DS:BX=串地址
mov cl,[bx]
or cl,cl ;cl=0 ?
jz .exit ;是的,返回主程序
call put_char
inc bx ;下一个字符
jmp put_string

.exit:
ret

;-------------------------------------------------------------------------------
put_char: ;显示一个字符
;输入:cl=字符ascii
push ax
push bx
push cx
push dx
push ds
push es

;以下取当前光标位置
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
in al,dx ;高8位
mov ah,al

mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
in al,dx ;低8位
mov bx,ax ;BX=代表光标位置的16位数

cmp cl,0x0d ;回车符?
jnz .put_0a ;不是。看看是不是换行等字符
mov ax,bx ;此句略显多余,但去掉后还得改书,麻烦
mov bl,80
div bl
mul bl
mov bx,ax
jmp .set_cursor

.put_0a:
cmp cl,0x0a ;换行符?
jnz .put_other ;不是,那就正常显示字符
add bx,80
jmp .roll_screen

.put_other: ;正常显示字符
mov ax,0xb800
mov es,ax
shl bx,1
mov [es:bx],cl

;以下将光标位置推进一个字符
shr bx,1
add bx,1

.roll_screen:
cmp bx,2000 ;光标超出屏幕?滚屏
jl .set_cursor

mov ax,0xb800
mov ds,ax
mov es,ax
cld
mov si,0xa0
mov di,0x00
mov cx,1920
rep movsw
mov bx,3840 ;清除屏幕最底一行
mov cx,80
.cls:
mov word[es:bx],0x0720
add bx,2
loop .cls

mov bx,1920

.set_cursor:
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
mov al,bh
out dx,al
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
mov al,bl
out dx,al

pop es
pop ds
pop dx
pop cx
pop bx
pop ax

ret

;-------------------------------------------------------------------------------
start:
;初始执行时,DS和ES指向用户程序头部段
mov ax,[stack_segment] ;设置到用户程序自己的堆栈
mov ss,ax
mov sp,stack_end
mov ax,[data_1_segment] ;设置到用户程序自己的数据段
mov ds,ax

mov bx,msg0
call put_string ;显示第一段信息

push word [es:code_2_segment]
mov ax,begin
push ax ;可以直接push begin,80386+
retf ;转移到代码段2执行
continue:
mov ax,[es:data_2_segment] ;段寄存器DS切换到数据段2
mov ds,ax
mov bx,msg1
call put_string ;显示第二段信息

jmp $

;===============================================================================
SECTION code_2 align=16 vstart=0 ;定义代码段2(16字节对齐)

begin:
push word [es:code_1_segment]
mov ax,continue
push ax ;可以直接push continue,80386+
retf ;转移到代码段1接着执行
;===============================================================================
SECTION data_1 align=16 vstart=0

msg0 db ' This is NASM - the famous Netwide Assembler. '
db 'Back at SourceForge and in intensive development! '
db 'Get the current versions from http://www.nasm.us/.'
db 0x0d,0x0a,0x0d,0x0a
db ' Example code for calculate 1+2+...+1000:',0x0d,0x0a,0x0d,0x0a
db ' xor dx,dx',0x0d,0x0a
db ' xor ax,ax',0x0d,0x0a
db ' xor cx,cx',0x0d,0x0a
db ' @@:',0x0d,0x0a
db ' inc cx',0x0d,0x0a
db ' add ax,cx',0x0d,0x0a
db ' adc dx,0',0x0d,0x0a
db ' inc cx',0x0d,0x0a
db ' cmp cx,1000',0x0d,0x0a
db ' jle @@',0x0d,0x0a
db ' ... ...(Some other codes)',0x0d,0x0a,0x0d,0x0a
db 0

;===============================================================================
SECTION data_2 align=16 vstart=0

msg1 db ' The above contents is written by LeeChung. '
db '2011-05-06'
db 0

;===============================================================================
SECTION stack align=16 vstart=0
resb 256

stack_end:

;===============================================================================
SECTION trail align=16
program_end:

+ 247
- 0
c08/c08.lst View File

@@ -0,0 +1,247 @@
1 ;代码清单8-2
2 ;文件名:c08.asm
3 ;文件说明:用户程序
4 ;创建日期:2011-5-5 18:17
5
6 ;===============================================================================
7 SECTION header vstart=0 ;定义用户程序头部段
8 00000000 [00000000] program_length dd program_end ;程序总长度[0x00]
9
10 ;用户程序入口点
11 00000004 [A000] code_entry dw start ;偏移地址[0x04]
12 00000006 [00000000] dd section.code_1.start ;段地址[0x06]
13
14 0000000A 0500 realloc_tbl_len dw (header_end-code_1_segment)/4
15 ;段重定位表项个数[0x0a]
16
17 ;段重定位表
18 0000000C [00000000] code_1_segment dd section.code_1.start ;[0x0c]
19 00000010 [00000000] code_2_segment dd section.code_2.start ;[0x10]
20 00000014 [00000000] data_1_segment dd section.data_1.start ;[0x14]
21 00000018 [00000000] data_2_segment dd section.data_2.start ;[0x18]
22 0000001C [00000000] stack_segment dd section.stack.start ;[0x1c]
23
24 header_end:
25
26 ;===============================================================================
27 SECTION code_1 align=16 vstart=0 ;定义代码段1(16字节对齐)
28 put_string: ;显示串(0结尾)。
29 ;输入:DS:BX=串地址
30 00000000 8A0F mov cl,[bx]
31 00000002 08C9 or cl,cl ;cl=0 ?
32 00000004 7406 jz .exit ;是的,返回主程序
33 00000006 E80400 call put_char
34 00000009 43 inc bx ;下一个字符
35 0000000A EBF4 jmp put_string
36
37 .exit:
38 0000000C C3 ret
39
40 ;-------------------------------------------------------------------------------
41 put_char: ;显示一个字符
42 ;输入:cl=字符ascii
43 0000000D 50 push ax
44 0000000E 53 push bx
45 0000000F 51 push cx
46 00000010 52 push dx
47 00000011 1E push ds
48 00000012 06 push es
49
50 ;以下取当前光标位置
51 00000013 BAD403 mov dx,0x3d4
52 00000016 B00E mov al,0x0e
53 00000018 EE out dx,al
54 00000019 BAD503 mov dx,0x3d5
55 0000001C EC in al,dx ;高8位
56 0000001D 88C4 mov ah,al
57
58 0000001F BAD403 mov dx,0x3d4
59 00000022 B00F mov al,0x0f
60 00000024 EE out dx,al
61 00000025 BAD503 mov dx,0x3d5
62 00000028 EC in al,dx ;低8位
63 00000029 89C3 mov bx,ax ;BX=代表光标位置的16位数
64
65 0000002B 80F90D cmp cl,0x0d ;回车符?
66 0000002E 750C jnz .put_0a ;不是。看看是不是换行等字符
67 00000030 89D8 mov ax,bx ;此句略显多余,但去掉后还得改书,麻烦
68 00000032 B350 mov bl,80
69 00000034 F6F3 div bl
70 00000036 F6E3 mul bl
71 00000038 89C3 mov bx,ax
72 0000003A EB45 jmp .set_cursor
73
74 .put_0a:
75 0000003C 80F90A cmp cl,0x0a ;换行符?
76 0000003F 7505 jnz .put_other ;不是,那就正常显示字符
77 00000041 83C350 add bx,80
78 00000044 EB0F jmp .roll_screen
79
80 .put_other: ;正常显示字符
81 00000046 B800B8 mov ax,0xb800
82 00000049 8EC0 mov es,ax
83 0000004B D1E3 shl bx,1
84 0000004D 26880F mov [es:bx],cl
85
86 ;以下将光标位置推进一个字符
87 00000050 D1EB shr bx,1
88 00000052 83C301 add bx,1
89
90 .roll_screen:
91 00000055 81FBD007 cmp bx,2000 ;光标超出屏幕?滚屏
92 00000059 7C26 jl .set_cursor
93
94 0000005B B800B8 mov ax,0xb800
95 0000005E 8ED8 mov ds,ax
96 00000060 8EC0 mov es,ax
97 00000062 FC cld
98 00000063 BEA000 mov si,0xa0
99 00000066 BF0000 mov di,0x00
100 00000069 B98007 mov cx,1920
101 0000006C F3A5 rep movsw
102 0000006E BB000F mov bx,3840 ;清除屏幕最底一行
103 00000071 B95000 mov cx,80
104 .cls:
105 00000074 26C7072007 mov word[es:bx],0x0720
106 00000079 83C302 add bx,2
107 0000007C E2F6 loop .cls
108
109 0000007E BB8007 mov bx,1920
110
111 .set_cursor:
112 00000081 BAD403 mov dx,0x3d4
113 00000084 B00E mov al,0x0e
114 00000086 EE out dx,al
115 00000087 BAD503 mov dx,0x3d5
116 0000008A 88F8 mov al,bh
117 0000008C EE out dx,al
118 0000008D BAD403 mov dx,0x3d4
119 00000090 B00F mov al,0x0f
120 00000092 EE out dx,al
121 00000093 BAD503 mov dx,0x3d5
122 00000096 88D8 mov al,bl
123 00000098 EE out dx,al
124
125 00000099 07 pop es
126 0000009A 1F pop ds
127 0000009B 5A pop dx
128 0000009C 59 pop cx
129 0000009D 5B pop bx
130 0000009E 58 pop ax
131
132 0000009F C3 ret
133
134 ;-------------------------------------------------------------------------------
135 start:
136 ;初始执行时,DS和ES指向用户程序头部段
137 000000A0 A1[1C00] mov ax,[stack_segment] ;设置到用户程序自己的堆栈
138 000000A3 8ED0 mov ss,ax
139 000000A5 BC[0001] mov sp,stack_end
140
141 000000A8 A1[1400] mov ax,[data_1_segment] ;设置到用户程序自己的数据段
142 000000AB 8ED8 mov ds,ax
143
144 000000AD BB[0000] mov bx,msg0
145 000000B0 E84DFF call put_string ;显示第一段信息
146
147 000000B3 26FF36[1000] push word [es:code_2_segment]
148 000000B8 B8[0000] mov ax,begin
149 000000BB 50 push ax ;可以直接push begin,80386+
150
151 000000BC CB retf ;转移到代码段2执行
152
153 continue:
154 000000BD 26A1[1800] mov ax,[es:data_2_segment] ;段寄存器DS切换到数据段2
155 000000C1 8ED8 mov ds,ax
156
157 000000C3 BB[0000] mov bx,msg1
158 000000C6 E837FF call put_string ;显示第二段信息
159
160 000000C9 EBFE jmp $
161
162 ;===============================================================================
163 SECTION code_2 align=16 vstart=0 ;定义代码段2(16字节对齐)
164
165 begin:
166 00000000 26FF36[0C00] push word [es:code_1_segment]
167 00000005 B8[BD00] mov ax,continue
168 00000008 50 push ax ;可以直接push continue,80386+
169
170 00000009 CB retf ;转移到代码段1接着执行
171
172 ;===============================================================================
173 SECTION data_1 align=16 vstart=0
174
175 00000000 202054686973206973- msg0 db ' This is NASM - the famous Netwide Assembler. '
176 00000009 204E41534D202D2074-
177 00000012 68652066616D6F7573-
178 0000001B 204E65747769646520-
179 00000024 417373656D626C6572-
180 0000002D 2E20
181 0000002F 4261636B2061742053- db 'Back at SourceForge and in intensive development! '
182 00000038 6F75726365466F7267-
183 00000041 6520616E6420696E20-
184 0000004A 696E74656E73697665-
185 00000053 20646576656C6F706D-
186 0000005C 656E742120
187 00000061 476574207468652063- db 'Get the current versions from http://www.nasm.us/.'
188 0000006A 757272656E74207665-
189 00000073 7273696F6E73206672-
190 0000007C 6F6D20687474703A2F-
191 00000085 2F7777772E6E61736D-
192 0000008E 2E75732F2E
193 00000093 0D0A0D0A db 0x0d,0x0a,0x0d,0x0a
194 00000097 20204578616D706C65- db ' Example code for calculate 1+2+...+1000:',0x0d,0x0a,0x0d,0x0a
195 000000A0 20636F646520666F72-
196 000000A9 2063616C63756C6174-
197 000000B2 6520312B322B2E2E2E-
198 000000BB 2B313030303A0D0A0D-
199 000000C4 0A
200 000000C5 2020202020786F7220- db ' xor dx,dx',0x0d,0x0a
201 000000CE 64782C64780D0A
202 000000D5 2020202020786F7220- db ' xor ax,ax',0x0d,0x0a
203 000000DE 61782C61780D0A
204 000000E5 2020202020786F7220- db ' xor cx,cx',0x0d,0x0a
205 000000EE 63782C63780D0A
206 000000F5 202040403A0D0A db ' @@:',0x0d,0x0a
207 000000FC 2020202020696E6320- db ' inc cx',0x0d,0x0a
208 00000105 63780D0A
209 00000109 202020202061646420- db ' add ax,cx',0x0d,0x0a
210 00000112 61782C63780D0A
211 00000119 202020202061646320- db ' adc dx,0',0x0d,0x0a
212 00000122 64782C300D0A
213 00000128 2020202020696E6320- db ' inc cx',0x0d,0x0a
214 00000131 63780D0A
215 00000135 2020202020636D7020- db ' cmp cx,1000',0x0d,0x0a
216 0000013E 63782C313030300D0A
217 00000147 20202020206A6C6520- db ' jle @@',0x0d,0x0a
218 00000150 40400D0A
219 00000154 20202020202E2E2E20- db ' ... ...(Some other codes)',0x0d,0x0a,0x0d,0x0a
220 0000015D 2E2E2E28536F6D6520-
221 00000166 6F7468657220636F64-
222 0000016F 6573290D0A0D0A
223 00000176 00 db 0
224
225 ;===============================================================================
226 SECTION data_2 align=16 vstart=0
227
228 00000000 20205468652061626F- msg1 db ' The above contents is written by LeeChung. '
229 00000009 766520636F6E74656E-
230 00000012 747320697320777269-
231 0000001B 7474656E206279204C-
232 00000024 65654368756E672E20
233 0000002D 323031312D30352D30- db '2011-05-06'
234 00000036 36
235 00000037 00 db 0
236
237 ;===============================================================================
238 SECTION stack align=16 vstart=0
239
240 00000000 <res 00000100> resb 256
241 ****************** warning: uninitialized space declared in stack section: zeroing
242
243 stack_end:
244
245 ;===============================================================================
246 SECTION trail align=16
247 program_end:

+ 154
- 0
c08/c08_mbr.asm View File

@@ -0,0 +1,154 @@
;代码清单8-1
;文件名:c08_mbr.asm
;文件说明:硬盘主引导扇区代码(加载程序)
;创建日期:2011-5-5 18:17
app_lba_start equ 1 ;声明常数(用户程序起始逻辑扇区号)
;常数的声明不会占用汇编地址
SECTION mbr align=16 vstart=0x7c00

;设置堆栈段和栈指针
mov ax,0
mov ss,ax
mov sp,ax
mov ax,[cs:phy_base] ;计算用于加载用户程序的逻辑段地址
mov dx,[cs:phy_base+0x02]
mov bx,16
div bx
mov ds,ax ;令DS和ES指向该段以进行操作
mov es,ax
;以下读取程序的起始部分
xor di,di
mov si,app_lba_start ;程序在硬盘上的起始逻辑扇区号
xor bx,bx ;加载到DS:0x0000处
call read_hard_disk_0
;以下判断整个程序有多大
mov dx,[2] ;曾经把dx写成了ds,花了二十分钟排错
mov ax,[0]
mov bx,512 ;512字节每扇区
div bx
cmp dx,0
jnz @1 ;未除尽,因此结果比实际扇区数少1
dec ax ;已经读了一个扇区,扇区总数减1
@1:
cmp ax,0 ;考虑实际长度小于等于512个字节的情况
jz direct
;读取剩余的扇区
push ds ;以下要用到并改变DS寄存器

mov cx,ax ;循环次数(剩余扇区数)
@2:
mov ax,ds
add ax,0x20 ;得到下一个以512字节为边界的段地址
mov ds,ax
xor bx,bx ;每次读时,偏移地址始终为0x0000
inc si ;下一个逻辑扇区
call read_hard_disk_0
loop @2 ;循环读,直到读完整个功能程序

pop ds ;恢复数据段基址到用户程序头部段
;计算入口点代码段基址
direct:
mov dx,[0x08]
mov ax,[0x06]
call calc_segment_base
mov [0x06],ax ;回填修正后的入口点代码段基址
;开始处理段重定位表
mov cx,[0x0a] ;需要重定位的项目数量
mov bx,0x0c ;重定位表首地址
realloc:
mov dx,[bx+0x02] ;32位地址的高16位
mov ax,[bx]
call calc_segment_base
mov [bx],ax ;回填段的基址
add bx,4 ;下一个重定位项(每项占4个字节)
loop realloc
jmp far [0x04] ;转移到用户程序
;-------------------------------------------------------------------------------
read_hard_disk_0: ;从硬盘读取一个逻辑扇区
;输入:DI:SI=起始逻辑扇区号
; DS:BX=目标缓冲区地址
push ax
push bx
push cx
push dx
mov dx,0x1f2
mov al,1
out dx,al ;读取的扇区数

inc dx ;0x1f3
mov ax,si ; read sector 1
out dx,al ;LBA地址7~0

inc dx ;0x1f4
mov al,0 ; 0, since lower 8bit is enough for sector 1
out dx,al ;LBA地址15~8

inc dx ;0x1f5
mov ax,0 ; 0, since lower 8bit is enough for sector 1
out dx,al ;LBA地址23~16

inc dx ;0x1f6
mov al,0xe0 ;LBA28模式,主盘
or al,ah ;LBA地址27~24
out dx,al

inc dx ;0x1f7
mov al,0x20 ;读命令
out dx,al

.waits:
in al,dx
and al,0x88
cmp al,0x08
jnz .waits ;不忙,且硬盘已准备好数据传输

mov cx,256 ;总共要读取的字数
mov dx,0x1f0
.readw:
in ax,dx
mov [bx],ax
add bx,2
loop .readw

pop dx
pop cx
pop bx
pop ax
ret

;-------------------------------------------------------------------------------
calc_segment_base: ;计算16位段地址
;输入:DX:AX=32位物理地址
;返回:AX=16位段基地址
push dx
add ax,[cs:phy_base]
adc dx,[cs:phy_base+0x02]
shr ax,4
ror dx,4
and dx,0xf000
or ax,dx
pop dx
ret

;-------------------------------------------------------------------------------
phy_base dd 0x10000 ;用户程序被加载的物理起始地址
times 510-($-$$) db 0
db 0x55,0xaa

+ 154
- 0
c08/c08_mbr.lst View File

@@ -0,0 +1,154 @@
1 ;代码清单8-1
2 ;文件名:c08_mbr.asm
3 ;文件说明:硬盘主引导扇区代码(加载程序)
4 ;创建日期:2011-5-5 18:17
5
6 app_lba_start equ 1 ;声明常数(用户程序起始逻辑扇区号)
7 ;常数的声明不会占用汇编地址
8
9 SECTION mbr align=16 vstart=0x7c00
10
11 ;设置堆栈段和栈指针
12 00000000 B80000 mov ax,0
13 00000003 8ED0 mov ss,ax
14 00000005 89C4 mov sp,ax
15
16 00000007 2EA1[C800] mov ax,[cs:phy_base] ;计算用于加载用户程序的逻辑段地址
17 0000000B 2E8B16[CA00] mov dx,[cs:phy_base+0x02]
18 00000010 BB1000 mov bx,16
19 00000013 F7F3 div bx
20 00000015 8ED8 mov ds,ax ;令DS和ES指向该段以进行操作
21 00000017 8EC0 mov es,ax
22
23 ;以下读取程序的起始部分
24 00000019 31FF xor di,di
25 0000001B BE0100 mov si,app_lba_start ;程序在硬盘上的起始逻辑扇区号
26 0000001E 31DB xor bx,bx ;加载到DS:0x0000处
27 00000020 E85100 call read_hard_disk_0
28
29 ;以下判断整个程序有多大
30 00000023 8B160200 mov dx,[2] ;曾经把dx写成了ds,花了二十分钟排错
31 00000027 A10000 mov ax,[0]
32 0000002A BB0002 mov bx,512 ;512字节每扇区
33 0000002D F7F3 div bx
34 0000002F 83FA00 cmp dx,0
35 00000032 7501 jnz @1 ;未除尽,因此结果比实际扇区数少1
36 00000034 48 dec ax ;已经读了一个扇区,扇区总数减1
37 @1:
38 00000035 83F800 cmp ax,0 ;考虑实际长度小于等于512个字节的情况
39 00000038 7413 jz direct
40
41 ;读取剩余的扇区
42 0000003A 1E push ds ;以下要用到并改变DS寄存器
43
44 0000003B 89C1 mov cx,ax ;循环次数(剩余扇区数)
45 @2:
46 0000003D 8CD8 mov ax,ds
47 0000003F 83C020 add ax,0x20 ;得到下一个以512字节为边界的段地址
48 00000042 8ED8 mov ds,ax
49
50 00000044 31DB xor bx,bx ;每次读时,偏移地址始终为0x0000
51 00000046 46 inc si ;下一个逻辑扇区
52 00000047 E82A00 call read_hard_disk_0
53 0000004A E2F1 loop @2 ;循环读,直到读完整个功能程序
54
55 0000004C 1F pop ds ;恢复数据段基址到用户程序头部段
56
57 ;计算入口点代码段基址
58 direct:
59 0000004D 8B160800 mov dx,[0x08]
60 00000051 A10600 mov ax,[0x06]
61 00000054 E85800 call calc_segment_base
62 00000057 A30600 mov [0x06],ax ;回填修正后的入口点代码段基址
63
64 ;开始处理段重定位表
65 0000005A 8B0E0A00 mov cx,[0x0a] ;需要重定位的项目数量
66 0000005E BB0C00 mov bx,0x0c ;重定位表首地址
67
68 realloc:
69 00000061 8B5702 mov dx,[bx+0x02] ;32位地址的高16位
70 00000064 8B07 mov ax,[bx]
71 00000066 E84600 call calc_segment_base
72 00000069 8907 mov [bx],ax ;回填段的基址
73 0000006B 83C304 add bx,4 ;下一个重定位项(每项占4个字节)
74 0000006E E2F1 loop realloc
75
76 00000070 FF2E0400 jmp far [0x04] ;转移到用户程序
77
78 ;-------------------------------------------------------------------------------
79 read_hard_disk_0: ;从硬盘读取一个逻辑扇区
80 ;输入:DI:SI=起始逻辑扇区号
81 ; DS:BX=目标缓冲区地址
82 00000074 50 push ax
83 00000075 53 push bx
84 00000076 51 push cx
85 00000077 52 push dx
86
87 00000078 BAF201 mov dx,0x1f2
88 0000007B B001 mov al,1
89 0000007D EE out dx,al ;读取的扇区数
90
91 0000007E 42 inc dx ;0x1f3
92 0000007F 89F0 mov ax,si ; read sector 100
93 00000081 EE out dx,al ;LBA地址7~0
94
95 00000082 42 inc dx ;0x1f4
96 00000083 B000 mov al,0 ; 0, since lower 8bit is enough for sector 100
97 00000085 EE out dx,al ;LBA地址15~8
98
99 00000086 42 inc dx ;0x1f5
100 00000087 B80000 mov ax,0 ; 0, since lower 8bit is enough for sector 100
101 0000008A EE out dx,al ;LBA地址23~16
102
103 0000008B 42 inc dx ;0x1f6
104 0000008C B0E0 mov al,0xe0 ;LBA28模式,主盘
105 0000008E 08E0 or al,ah ;LBA地址27~24
106 00000090 EE out dx,al
107
108 00000091 42 inc dx ;0x1f7
109 00000092 B020 mov al,0x20 ;读命令
110 00000094 EE out dx,al
111
112 .waits:
113 00000095 EC in al,dx
114 00000096 2488 and al,0x88
115 00000098 3C08 cmp al,0x08
116 0000009A 75F9 jnz .waits ;不忙,且硬盘已准备好数据传输
117
118 0000009C B90001 mov cx,256 ;总共要读取的字数
119 0000009F BAF001 mov dx,0x1f0
120 .readw:
121 000000A2 ED in ax,dx
122 000000A3 8907 mov [bx],ax
123 000000A5 83C302 add bx,2
124 000000A8 E2F8 loop .readw
125
126 000000AA 5A pop dx
127 000000AB 59 pop cx
128 000000AC 5B pop bx
129 000000AD 58 pop ax
130
131 000000AE C3 ret
132
133 ;-------------------------------------------------------------------------------
134 calc_segment_base: ;计算16位段地址
135 ;输入:DX:AX=32位物理地址
136 ;返回:AX=16位段基地址
137 000000AF 52 push dx
138
139 000000B0 2E0306[C800] add ax,[cs:phy_base]
140 000000B5 2E1316[CA00] adc dx,[cs:phy_base+0x02]
141 000000BA C1E804 shr ax,4
142 000000BD C1CA04 ror dx,4
143 000000C0 81E200F0 and dx,0xf000
144 000000C4 09D0 or ax,dx
145
146 000000C6 5A pop dx
147
148 000000C7 C3 ret
149
150 ;-------------------------------------------------------------------------------
151 000000C8 00000100 phy_base dd 0x10000 ;用户程序被加载的物理起始地址
152
153 000000CC 00<rep 132h> times 510-($-$$) db 0
154 000001FE 55AA db 0x55,0xaa

+ 304
- 0
c09/c09_1.asm View File

@@ -0,0 +1,304 @@
;代码清单9-1
;文件名:c09_1.asm
;文件说明:用户程序
;创建日期:2011-4-16 22:03
;===============================================================================
SECTION header vstart=0 ;定义用户程序头部段
program_length dd program_end ;程序总长度[0x00]
;用户程序入口点
code_entry dw start ;偏移地址[0x04]
dd section.code.start ;段地址[0x06]
realloc_tbl_len dw (header_end-realloc_begin)/4
;段重定位表项个数[0x0a]
realloc_begin:
;段重定位表
code_segment dd section.code.start ;[0x0c]
data_segment dd section.data.start ;[0x14]
stack_segment dd section.stack.start ;[0x1c]
header_end:
;===============================================================================
SECTION code align=16 vstart=0 ;定义代码段(16字节对齐)
new_int_0x70:
push ax
push bx
push cx
push dx
push es
.w0:
mov al,0x0a ;阻断NMI。当然,通常是不必要的
or al,0x80
out 0x70,al
in al,0x71 ;读寄存器A
test al,0x80 ;测试第7位UIP
jnz .w0 ;以上代码对于更新周期结束中断来说
;是不必要的
xor al,al
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(秒)
push ax

mov al,2
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(分)
push ax

mov al,4
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(时)
push ax

mov al,0x0c ;寄存器C的索引。且开放NMI
out 0x70,al
in al,0x71 ;读一下RTC的寄存器C,否则只发生一次中断
;此处不考虑闹钟和周期性中断的情况
mov ax,0xb800
mov es,ax

pop ax
call bcd_to_ascii
mov bx,12*160 + 36*2 ;从屏幕上的12行36列开始显示

mov [es:bx],ah
mov [es:bx+2],al ;显示两位小时数字

mov al,':'
mov [es:bx+4],al ;显示分隔符':'
not byte [es:bx+5] ;反转显示属性

pop ax
call bcd_to_ascii
mov [es:bx+6],ah
mov [es:bx+8],al ;显示两位分钟数字

mov al,':'
mov [es:bx+10],al ;显示分隔符':'
not byte [es:bx+11] ;反转显示属性

pop ax
call bcd_to_ascii
mov [es:bx+12],ah
mov [es:bx+14],al ;显示两位小时数字
mov al,0x20 ;中断结束命令EOI
out 0xa0,al ;向从片发送
out 0x20,al ;向主片发送

pop es
pop dx
pop cx
pop bx
pop ax

iret

;-------------------------------------------------------------------------------
bcd_to_ascii: ;BCD码转ASCII
;输入:AL=bcd码
;输出:AX=ascii
mov ah,al ;分拆成两个数字
and al,0x0f ;仅保留低4位
add al,0x30 ;转换成ASCII

shr ah,4 ;逻辑右移4位
and ah,0x0f
add ah,0x30

ret

;-------------------------------------------------------------------------------
start:
mov ax,[stack_segment]
mov ss,ax
mov sp,ss_pointer
mov ax,[data_segment]
mov ds,ax
mov bx,init_msg ;显示初始信息
call put_string

mov bx,inst_msg ;显示安装信息
call put_string
mov al,0x70
mov bl,4
mul bl ;计算0x70号中断在IVT中的偏移
mov bx,ax

cli ;防止改动期间发生新的0x70号中断

push es
mov ax,0x0000
mov es,ax
mov word [es:bx],new_int_0x70 ;偏移地址。
mov word [es:bx+2],cs ;段地址
pop es

mov al,0x0b ;RTC寄存器B
or al,0x80 ;阻断NMI
out 0x70,al
mov al,0x12 ;设置寄存器B,禁止周期性中断,开放更
out 0x71,al ;新结束后中断,BCD码,24小时制

mov al,0x0c
out 0x70,al
in al,0x71 ;读RTC寄存器C,复位未决的中断状态

in al,0xa1 ;读8259从片的IMR寄存器
and al,0xfe ;清除bit 0(此位连接RTC)
out 0xa1,al ;写回此寄存器

sti ;重新开放中断

mov bx,done_msg ;显示安装完成信息
call put_string

mov bx,tips_msg ;显示提示信息
call put_string
mov cx,0xb800
mov ds,cx
mov byte [12*160 + 33*2],'@' ;屏幕第12行,35列
.idle:
hlt ;使CPU进入低功耗状态,直到用中断唤醒
not byte [12*160 + 33*2+1] ;反转显示属性
jmp .idle

;-------------------------------------------------------------------------------
put_string: ;显示串(0结尾)。
;输入:DS:BX=串地址
mov cl,[bx]
or cl,cl ;cl=0 ?
jz .exit ;是的,返回主程序
call put_char
inc bx ;下一个字符
jmp put_string

.exit:
ret

;-------------------------------------------------------------------------------
put_char: ;显示一个字符
;输入:cl=字符ascii
push ax
push bx
push cx
push dx
push ds
push es

;以下取当前光标位置
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
in al,dx ;高8位
mov ah,al

mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
in al,dx ;低8位
mov bx,ax ;BX=代表光标位置的16位数

cmp cl,0x0d ;回车符?
jnz .put_0a ;不是。看看是不是换行等字符
mov ax,bx ;
mov bl,80
div bl
mul bl
mov bx,ax
jmp .set_cursor

.put_0a:
cmp cl,0x0a ;换行符?
jnz .put_other ;不是,那就正常显示字符
add bx,80
jmp .roll_screen

.put_other: ;正常显示字符
mov ax,0xb800
mov es,ax
shl bx,1
mov [es:bx],cl

;以下将光标位置推进一个字符
shr bx,1
add bx,1

.roll_screen:
cmp bx,2000 ;光标超出屏幕?滚屏
jl .set_cursor

mov ax,0xb800
mov ds,ax
mov es,ax
cld
mov si,0xa0
mov di,0x00
mov cx,1920
rep movsw
mov bx,3840 ;清除屏幕最底一行
mov cx,80
.cls:
mov word[es:bx],0x0720
add bx,2
loop .cls

mov bx,1920

.set_cursor:
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
mov al,bh
out dx,al
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
mov al,bl
out dx,al

pop es
pop ds
pop dx
pop cx
pop bx
pop ax

ret

;===============================================================================
SECTION data align=16 vstart=0

init_msg db 'Starting...',0x0d,0x0a,0
inst_msg db 'Installing a new interrupt 70H...',0
done_msg db 'Done.',0x0d,0x0a,0

tips_msg db 'Clock is now working.',0
;===============================================================================
SECTION stack align=16 vstart=0
resb 256
ss_pointer:
;===============================================================================
SECTION program_trail
program_end:

+ 71
- 0
c09/c09_2.asm View File

@@ -0,0 +1,71 @@
;代码清单9-2
;文件名:c09_2.asm
;文件说明:用于演示BIOS中断的用户程序
;创建日期:2012-3-28 20:35
;===============================================================================
SECTION header vstart=0 ;定义用户程序头部段
program_length dd program_end ;程序总长度[0x00]
;用户程序入口点
code_entry dw start ;偏移地址[0x04]
dd section.code.start ;段地址[0x06]
realloc_tbl_len dw (header_end-realloc_begin)/4
;段重定位表项个数[0x0a]
realloc_begin:
;段重定位表
code_segment dd section.code.start ;[0x0c]
data_segment dd section.data.start ;[0x14]
stack_segment dd section.stack.start ;[0x1c]
header_end:
;===============================================================================
SECTION code align=16 vstart=0 ;定义代码段(16字节对齐)
start:
mov ax,[stack_segment]
mov ss,ax
mov sp,ss_pointer
mov ax,[data_segment]
mov ds,ax
mov cx,msg_end-message
mov bx,message
.putc:
mov ah,0x0e
mov al,[bx]
int 0x10
inc bx
loop .putc

.reps:
mov ah,0x00
int 0x16
mov ah,0x0e
mov bl,0x07
int 0x10

jmp .reps

;===============================================================================
SECTION data align=16 vstart=0

message db 'Hello, friend!',0x0d,0x0a
db 'This simple procedure used to demonstrate '
db 'the BIOS interrupt.',0x0d,0x0a
db 'Please press the keys on the keyboard ->'
msg_end:
;===============================================================================
SECTION stack align=16 vstart=0
resb 256
ss_pointer:
;===============================================================================
SECTION program_trail
program_end:

+ 99
- 0
c11/c11_mbr.asm View File

@@ -0,0 +1,99 @@
;代码清单11-1
;文件名:c11_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-5-16 19:54

;设置堆栈段和栈指针
mov ax,cs
mov ss,ax
mov sp,0x7c00
;计算GDT所在的逻辑段地址
mov ax,[cs:gdt_base+0x7c00] ;低16位
mov dx,[cs:gdt_base+0x7c00+0x02] ;高16位
mov bx,16
div bx
mov ds,ax ;令DS指向该段以进行操作
mov bx,dx ;段内起始偏移地址
;创建0#描述符,它是空描述符,这是处理器的要求
mov dword [bx+0x00],0x00
mov dword [bx+0x04],0x00

;创建#1描述符,保护模式下的代码段描述符
mov dword [bx+0x08],0x7c0001ff
mov dword [bx+0x0c],0x00409800

;创建#2描述符,保护模式下的数据段描述符(文本模式下的显示缓冲区)
mov dword [bx+0x10],0x8000ffff
mov dword [bx+0x14],0x0040920b

;创建#3描述符,保护模式下的堆栈段描述符
mov dword [bx+0x18],0x00007a00
mov dword [bx+0x1c],0x00409600

;初始化描述符表寄存器GDTR
mov word [cs: gdt_size+0x7c00],31 ;描述符表的界限(总字节数减一)
lgdt [cs: gdt_size+0x7c00]
in al,0x92 ;南桥芯片内的端口
or al,0000_0010B
out 0x92,al ;打开A20

cli ;保护模式下中断机制尚未建立,应
;禁止中断
mov eax,cr0
or eax,1
mov cr0,eax ;设置PE位
;以下进入保护模式... ...
jmp dword 0x0008:flush ;16位的描述符选择子:32位偏移
;清流水线并串行化处理器
[bits 32]

flush:
mov cx,00000000000_10_000B ;加载数据段选择子(0x10)
mov ds,cx

;以下在屏幕上显示"Protect mode OK."
mov byte [0x00],'P'
mov byte [0x02],'r'
mov byte [0x04],'o'
mov byte [0x06],'t'
mov byte [0x08],'e'
mov byte [0x0a],'c'
mov byte [0x0c],'t'
mov byte [0x0e],' '
mov byte [0x10],'m'
mov byte [0x12],'o'
mov byte [0x14],'d'
mov byte [0x16],'e'
mov byte [0x18],' '
mov byte [0x1a],'O'
mov byte [0x1c],'K'

;以下用简单的示例来帮助阐述32位保护模式下的堆栈操作
mov cx,00000000000_11_000B ;加载堆栈段选择子
mov ss,cx
mov esp,0x7c00

mov ebp,esp ;保存堆栈指针
push byte '.' ;压入立即数(字节)
sub ebp,4
cmp ebp,esp ;判断压入立即数时,ESP是否减4
jnz ghalt
pop eax
mov [0x1e],al ;显示句点
ghalt:
hlt ;已经禁止中断,将不会被唤醒

;-------------------------------------------------------------------------------
gdt_size dw 0
gdt_base dd 0x00007e00 ;GDT的物理地址
times 510-($-$$) db 0
db 0x55,0xaa

+ 111
- 0
c12/c12_mbr.asm View File

@@ -0,0 +1,111 @@
;代码清单12-1
;文件名:c12_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-10-27 22:52

;设置堆栈段和栈指针
mov eax,cs
mov ss,eax
mov sp,0x7c00
;计算GDT所在的逻辑段地址
mov eax,[cs:pgdt+0x7c00+0x02] ;GDT的32位线性基地址
xor edx,edx
mov ebx,16
div ebx ;分解成16位逻辑地址

mov ds,eax ;令DS指向该段以进行操作
mov ebx,edx ;段内起始偏移地址

;创建0#描述符,它是空描述符,这是处理器的要求
mov dword [ebx+0x00],0x00000000
mov dword [ebx+0x04],0x00000000

;创建1#描述符,这是一个数据段,对应0~4GB的线性地址空间
mov dword [ebx+0x08],0x0000ffff ;基地址为0,段界限为0xfffff
mov dword [ebx+0x0c],0x00cf9200 ;粒度为4KB,存储器段描述符

;创建保护模式下初始代码段描述符
mov dword [ebx+0x10],0x7c0001ff ;基地址为0x00007c00,512字节
mov dword [ebx+0x14],0x00409800 ;粒度为1个字节,代码段描述符

;创建以上代码段的别名描述符
mov dword [ebx+0x18],0x7c0001ff ;基地址为0x00007c00,512字节
mov dword [ebx+0x1c],0x00409200 ;粒度为1个字节,数据段描述符

mov dword [ebx+0x20],0x7c00fffe
mov dword [ebx+0x24],0x00cf9600
;初始化描述符表寄存器GDTR
mov word [cs: pgdt+0x7c00],39 ;描述符表的界限
lgdt [cs: pgdt+0x7c00]
in al,0x92 ;南桥芯片内的端口
or al,0000_0010B
out 0x92,al ;打开A20

cli ;中断机制尚未工作

mov eax,cr0
or eax,1
mov cr0,eax ;设置PE位
;以下进入保护模式... ...
jmp dword 0x0010:flush ;16位的描述符选择子:32位偏移
[bits 32]
flush:
mov eax,0x0018
mov ds,eax
mov eax,0x0008 ;加载数据段(0..4GB)选择子
mov es,eax
mov fs,eax
mov gs,eax
mov eax,0x0020 ;0000 0000 0010 0000
mov ss,eax
xor esp,esp ;ESP <- 0
mov dword [es:0x0b8000],0x072e0750 ;字符'P'、'.'及其显示属性
mov dword [es:0x0b8004],0x072e074d ;字符'M'、'.'及其显示属性
mov dword [es:0x0b8008],0x07200720 ;两个空白字符及其显示属性
mov dword [es:0x0b800c],0x076b076f ;字符'o'、'k'及其显示属性

;开始冒泡排序
mov ecx,pgdt-string-1 ;遍历次数=串长度-1
@@1:
push ecx ;32位模式下的loop使用ecx
xor bx,bx ;32位模式下,偏移量可以是16位,也可以
@@2: ;是后面的32位
mov ax,[string+bx]
cmp ah,al ;ah中存放的是源字的高字节
jge @@3
xchg al,ah
mov [string+bx],ax
@@3:
inc bx
loop @@2
pop ecx
loop @@1
mov ecx,pgdt-string
xor ebx,ebx ;偏移地址是32位的情况
@@4: ;32位的偏移具有更大的灵活性
mov ah,0x07
mov al,[string+ebx]
mov [es:0xb80a0+ebx*2],ax ;演示0~4GB寻址。
inc ebx
loop @@4
hlt

;-------------------------------------------------------------------------------
string db 's0ke4or92xap3fv8giuzjcy5l1m7hd6bnqtw.'
;-------------------------------------------------------------------------------
pgdt dw 0
dd 0x00007e00 ;GDT的物理地址
;-------------------------------------------------------------------------------
times 510-($-$$) db 0
db 0x55,0xaa

+ 87
- 0
c13/c13.asm View File

@@ -0,0 +1,87 @@
;代码清单13-3
;文件名:c13.asm
;文件说明:用户程序
;创建日期:2011-10-30 15:19
;===============================================================================
SECTION header vstart=0

program_length dd program_end ;程序总长度#0x00
head_len dd header_end ;程序头部的长度#0x04

stack_seg dd 0 ;用于接收堆栈段选择子#0x08
stack_len dd 1 ;程序建议的堆栈大小#0x0c
;以4KB为单位
prgentry dd start ;程序入口#0x10
code_seg dd section.code.start ;代码段位置#0x14
code_len dd code_end ;代码段长度#0x18

data_seg dd section.data.start ;数据段位置#0x1c
data_len dd data_end ;数据段长度#0x20
;-------------------------------------------------------------------------------
;符号地址检索表
salt_items dd (header_end-salt)/256 ;#0x24
salt: ;#0x28
PrintString db '@PrintString'
times 256-($-PrintString) db 0
TerminateProgram db '@TerminateProgram'
times 256-($-TerminateProgram) db 0
ReadDiskData db '@ReadDiskData'
times 256-($-ReadDiskData) db 0
header_end:

;===============================================================================
SECTION data vstart=0
buffer times 1024 db 0 ;缓冲区

message_1 db 0x0d,0x0a,0x0d,0x0a
db '**********User program is runing**********'
db 0x0d,0x0a,0
message_2 db ' Disk data:',0x0d,0x0a,0

data_end:

;===============================================================================
[bits 32]
;===============================================================================
SECTION code vstart=0
start:
mov eax,ds
mov fs,eax
mov eax,[stack_seg]
mov ss,eax
mov esp,0
mov eax,[data_seg]
mov ds,eax
mov ebx,message_1
call far [fs:PrintString]
mov eax,100 ;逻辑扇区号100
mov ebx,buffer ;缓冲区偏移地址
call far [fs:ReadDiskData] ;段间调用
mov ebx,message_2
call far [fs:PrintString]
mov ebx,buffer
call far [fs:PrintString] ;too.
jmp far [fs:TerminateProgram] ;将控制权返回到系统
code_end:

;===============================================================================
SECTION trail
;-------------------------------------------------------------------------------
program_end:

+ 95
- 0
c13/c13.lst View File

@@ -0,0 +1,95 @@
1 ;代码清单13-3
2 ;文件名:c13.asm
3 ;文件说明:用户程序
4 ;创建日期:2011-10-30 15:19
5
6 ;===============================================================================
7 SECTION header vstart=0
8
9 00000000 [00000000] program_length dd program_end ;程序总长度#0x00
10
11 00000004 [28030000] head_len dd header_end ;程序头部的长度#0x04
12
13 00000008 00000000 stack_seg dd 0 ;用于接收堆栈段选择子#0x08
14 0000000C 01000000 stack_len dd 1 ;程序建议的堆栈大小#0x0c
15 ;以4KB为单位
16
17 00000010 [00000000] prgentry dd start ;程序入口#0x10
18 00000014 [00000000] code_seg dd section.code.start ;代码段位置#0x14
19 00000018 [53000000] code_len dd code_end ;代码段长度#0x18
20
21 0000001C [00000000] data_seg dd section.data.start ;数据段位置#0x1c
22 00000020 [40040000] data_len dd data_end ;数据段长度#0x20
23
24 ;-------------------------------------------------------------------------------
25 ;符号地址检索表
26 00000024 03000000 salt_items dd (header_end-salt)/256 ;#0x24
27
28 salt: ;#0x28
29 00000028 405072696E74537472- PrintString db '@PrintString'
30 00000031 696E67
31 00000034 00<rept> times 256-($-PrintString) db 0
32
33 00000128 405465726D696E6174- TerminateProgram db '@TerminateProgram'
34 00000131 6550726F6772616D
35 00000139 00<rept> times 256-($-TerminateProgram) db 0
36
37 00000228 40526561644469736B- ReadDiskData db '@ReadDiskData'
38 00000231 44617461
39 00000235 00<rept> times 256-($-ReadDiskData) db 0
40
41 header_end:
42
43 ;===============================================================================
44 SECTION data vstart=0
45
46 00000000 00<rept> buffer times 1024 db 0 ;缓冲区
47
48 00000400 0D0A0D0A message_1 db 0x0d,0x0a,0x0d,0x0a
49 00000404 2A2A2A2A2A2A2A2A2A- db '**********User program is runing**********'
50 0000040D 2A557365722070726F-
51 00000416 6772616D2069732072-
52 0000041F 756E696E672A2A2A2A-
53 00000428 2A2A2A2A2A2A
54 0000042E 0D0A00 db 0x0d,0x0a,0
55 00000431 20204469736B206461- message_2 db ' Disk data:',0x0d,0x0a,0
56 0000043A 74613A0D0A00
57
58 data_end:
59
60 ;===============================================================================
61 [bits 32]
62 ;===============================================================================
63 SECTION code vstart=0
64 start:
65 00000000 8CD8 mov eax,ds
66 00000002 8EE0 mov fs,eax
67
68 00000004 A1[08000000] mov eax,[stack_seg]
69 00000009 8ED0 mov ss,eax
70 0000000B BC00000000 mov esp,0
71
72 00000010 A1[1C000000] mov eax,[data_seg]
73 00000015 8ED8 mov ds,eax
74
75 00000017 BB[00040000] mov ebx,message_1
76 0000001C 64FF1D[28000000] call far [fs:PrintString]
77
78 00000023 B864000000 mov eax,100 ;逻辑扇区号100
79 00000028 BB[00000000] mov ebx,buffer ;缓冲区偏移地址
80 0000002D 64FF1D[28020000] call far [fs:ReadDiskData] ;段间调用
81
82 00000034 BB[31040000] mov ebx,message_2
83 00000039 64FF1D[28000000] call far [fs:PrintString]
84
85 00000040 BB[00000000] mov ebx,buffer
86 00000045 64FF1D[28000000] call far [fs:PrintString] ;too.
87
88 0000004C 64FF2D[28010000] jmp far [fs:TerminateProgram] ;将控制权返回到系统
89
90 code_end:
91
92 ;===============================================================================
93 SECTION trail
94 ;-------------------------------------------------------------------------------
95 program_end:

+ 601
- 0
c13/c13_core.asm View File

@@ -0,0 +1,601 @@
;代码清单13-2
;文件名:c13_core.asm
;文件说明:保护模式微型核心程序
;创建日期:2011-10-26 12:11

;以下常量定义部分。内核的大部分内容都应当固定
core_code_seg_sel equ 0x38 ;内核代码段选择子
core_data_seg_sel equ 0x30 ;内核数据段选择子
sys_routine_seg_sel equ 0x28 ;系统公共例程代码段的选择子
video_ram_seg_sel equ 0x20 ;视频显示缓冲区的段选择子
core_stack_seg_sel equ 0x18 ;内核堆栈段选择子
mem_0_4_gb_seg_sel equ 0x08 ;整个0-4GB内存的段的选择子

;-------------------------------------------------------------------------------
;以下是系统核心的头部,用于加载核心程序
core_length dd core_end ;核心程序总长度#00

sys_routine_seg dd section.sys_routine.start
;系统公用例程段位置#04

core_data_seg dd section.core_data.start
;核心数据段位置#08

core_code_seg dd section.core_code.start
;核心代码段位置#0c


core_entry dd start ;核心代码段入口点#10
dw core_code_seg_sel

;===============================================================================
[bits 32]
;===============================================================================
SECTION sys_routine vstart=0 ;系统公共例程代码段
;-------------------------------------------------------------------------------
;字符串显示例程
put_string: ;显示0终止的字符串并移动光标
;输入:DS:EBX=串地址
push ecx
.getc:
mov cl,[ebx]
or cl,cl
jz .exit
call put_char
inc ebx
jmp .getc

.exit:
pop ecx
retf ;段间返回

;-------------------------------------------------------------------------------
put_char: ;在当前光标处显示一个字符,并推进
;光标。仅用于段内调用
;输入:CL=字符ASCII码
pushad

;以下取当前光标位置
mov dx,0x3d4
mov al,0x0e
out dx,al
inc dx ;0x3d5
in al,dx ;高字
mov ah,al

dec dx ;0x3d4
mov al,0x0f
out dx,al
inc dx ;0x3d5
in al,dx ;低字
mov bx,ax ;BX=代表光标位置的16位数

cmp cl,0x0d ;回车符?
jnz .put_0a
mov ax,bx
mov bl,80
div bl
mul bl
mov bx,ax
jmp .set_cursor

.put_0a:
cmp cl,0x0a ;换行符?
jnz .put_other
add bx,80
jmp .roll_screen

.put_other: ;正常显示字符
push es
mov eax,video_ram_seg_sel ;0xb8000段的选择子
mov es,eax
shl bx,1
mov [es:bx],cl
pop es

;以下将光标位置推进一个字符
shr bx,1
inc bx

.roll_screen:
cmp bx,2000 ;光标超出屏幕?滚屏
jl .set_cursor

push ds
push es
mov eax,video_ram_seg_sel
mov ds,eax
mov es,eax
cld
mov esi,0xa0 ;小心!32位模式下movsb/w/d
mov edi,0x00 ;使用的是esi/edi/ecx
mov ecx,1920
rep movsd
mov bx,3840 ;清除屏幕最底一行
mov ecx,80 ;32位程序应该使用ECX
.cls:
mov word[es:bx],0x0720
add bx,2
loop .cls

pop es
pop ds

mov bx,1920

.set_cursor:
mov dx,0x3d4
mov al,0x0e
out dx,al
inc dx ;0x3d5
mov al,bh
out dx,al
dec dx ;0x3d4
mov al,0x0f
out dx,al
inc dx ;0x3d5
mov al,bl
out dx,al

popad
ret

;-------------------------------------------------------------------------------
read_hard_disk_0: ;从硬盘读取一个逻辑扇区
;EAX=逻辑扇区号
;DS:EBX=目标缓冲区地址
;返回:EBX=EBX+512
push eax
push ecx
push edx
push eax
mov dx,0x1f2
mov al,1
out dx,al ;读取的扇区数

inc dx ;0x1f3
pop eax
out dx,al ;LBA地址7~0

inc dx ;0x1f4
mov cl,8
shr eax,cl
out dx,al ;LBA地址15~8

inc dx ;0x1f5
shr eax,cl
out dx,al ;LBA地址23~16

inc dx ;0x1f6
shr eax,cl
or al,0xe0 ;第一硬盘 LBA地址27~24
out dx,al

inc dx ;0x1f7
mov al,0x20 ;读命令
out dx,al

.waits:
in al,dx
and al,0x88
cmp al,0x08
jnz .waits ;不忙,且硬盘已准备好数据传输

mov ecx,256 ;总共要读取的字数
mov dx,0x1f0
.readw:
in ax,dx
mov [ebx],ax
add ebx,2
loop .readw

pop edx
pop ecx
pop eax
retf ;段间返回

;-------------------------------------------------------------------------------
;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助
put_hex_dword: ;在当前光标处以十六进制形式显示
;一个双字并推进光标
;输入:EDX=要转换并显示的数字
;输出:无
pushad
push ds
mov ax,core_data_seg_sel ;切换到核心数据段
mov ds,ax
mov ebx,bin_hex ;指向核心数据段内的转换表
mov ecx,8
.xlt:
rol edx,4
mov eax,edx
and eax,0x0000000f
xlat
push ecx