├── README.md ├── booktool ├── HexView.exe ├── c05 │ ├── c05_mbr.asm │ ├── c05_mbr.bin │ ├── c05_mbr.img │ └── c05_mbr.lst ├── c06 │ ├── c06_mbr.asm │ ├── c06_mbr.bin │ └── c06_mbr.lst ├── c07 │ ├── c07_mbr.asm │ ├── c07_mbr.bin │ └── c07_mbr.lst ├── c08 │ ├── c08.asm │ ├── c08.bin │ ├── c08.lst │ ├── c08_mbr.asm │ ├── c08_mbr.bin │ └── c08_mbr.lst ├── c09 │ ├── c09_1.asm │ ├── c09_1.bin │ ├── c09_1.lst │ ├── c09_2.asm │ ├── c09_2.bin │ └── c09_2.lst ├── c11 │ └── c11_mbr.asm ├── c12 │ └── c12_mbr.asm ├── c13 │ ├── c13.asm │ ├── c13_core.asm │ ├── c13_mbr.asm │ └── diskdata.txt ├── c14 │ └── c14_core.asm ├── c15 │ ├── c15.asm │ └── c15_core.asm ├── c16 │ ├── c16.asm │ └── c16_core.asm ├── c17 │ ├── c17-1.asm │ ├── c17-2.asm │ ├── c17_core.asm │ ├── c17_mbr.asm │ └── c17_mbr_1.back ├── fixvhdwr.exe ├── nasmide.exe ├── nasmide.ini ├── ~$越计卷2目录.doc └── 相关文档 │ ├── SB-16声卡播放程序 │ ├── baby.wav │ ├── c10.asm │ ├── c10.bin │ ├── c10.lst │ └── 声霸卡硬件开发手册[英文].pdf │ ├── Virtual Hard Disk Format Spec_10_18_06.doc │ └── 原稿第10章 │ ├── baby.wav │ ├── c10.asm │ ├── c10.bin │ ├── c10.lst │ ├── 声霸卡硬件开发手册[英文].pdf │ └── 第十章 聆听数字的声音.doc ├── x86汇编语言-从实模式到保护模式.pdf └── 操作系统启动过程.md /README.md: -------------------------------------------------------------------------------- 1 | 本仓库是《x86汇编语言-从实模式到保护模式》一书中的源代码,章节对应代码和pdf都在相关目录下 2 | -------------------------------------------------------------------------------- /booktool/HexView.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/HexView.exe -------------------------------------------------------------------------------- /booktool/c05/c05_mbr.asm: -------------------------------------------------------------------------------- 1 | ;代码清单5-1 2 | ;文件名:c05_mbr.asm 3 | ;文件说明:硬盘主引导扇区代码 4 | ;创建日期:2011-3-31 21:15 5 | 6 | mov ax,0xb800 ;指向文本模式的显示缓冲区 7 | mov es,ax 8 | 9 | ;以下显示字符串"Label offset:" 10 | mov byte [es:0x00],'L' 11 | mov byte [es:0x01],0x07 12 | mov byte [es:0x02],'a' 13 | mov byte [es:0x03],0x07 14 | mov byte [es:0x04],'b' 15 | mov byte [es:0x05],0x07 16 | mov byte [es:0x06],'e' 17 | mov byte [es:0x07],0x07 18 | mov byte [es:0x08],'l' 19 | mov byte [es:0x09],0x07 20 | mov byte [es:0x0a],' ' 21 | mov byte [es:0x0b],0x07 22 | mov byte [es:0x0c],"o" 23 | mov byte [es:0x0d],0x07 24 | mov byte [es:0x0e],'f' 25 | mov byte [es:0x0f],0x07 26 | mov byte [es:0x10],'f' 27 | mov byte [es:0x11],0x07 28 | mov byte [es:0x12],'s' 29 | mov byte [es:0x13],0x07 30 | mov byte [es:0x14],'e' 31 | mov byte [es:0x15],0x07 32 | mov byte [es:0x16],'t' 33 | mov byte [es:0x17],0x07 34 | mov byte [es:0x18],':' 35 | mov byte [es:0x19],0x07 36 | 37 | mov ax,number ;取得标号number的偏移地址 38 | mov bx,10 39 | 40 | ;设置数据段的基地址 41 | mov cx,cs 42 | mov ds,cx 43 | 44 | ;求个位上的数字 45 | mov dx,0 46 | div bx 47 | mov [0x7c00+number+0x00],dl ;保存个位上的数字 48 | 49 | ;求十位上的数字 50 | xor dx,dx 51 | div bx 52 | mov [0x7c00+number+0x01],dl ;保存十位上的数字 53 | 54 | ;求百位上的数字 55 | xor dx,dx 56 | div bx 57 | mov [0x7c00+number+0x02],dl ;保存百位上的数字 58 | 59 | ;求千位上的数字 60 | xor dx,dx 61 | div bx 62 | mov [0x7c00+number+0x03],dl ;保存千位上的数字 63 | 64 | ;求万位上的数字 65 | xor dx,dx 66 | div bx 67 | mov [0x7c00+number+0x04],dl ;保存万位上的数字 68 | 69 | ;以下用十进制显示标号的偏移地址 70 | mov al,[0x7c00+number+0x04] 71 | add al,0x30 72 | mov [es:0x1a],al 73 | mov byte [es:0x1b],0x04 74 | 75 | mov al,[0x7c00+number+0x03] 76 | add al,0x30 77 | mov [es:0x1c],al 78 | mov byte [es:0x1d],0x04 79 | 80 | mov al,[0x7c00+number+0x02] 81 | add al,0x30 82 | mov [es:0x1e],al 83 | mov byte [es:0x1f],0x04 84 | 85 | mov al,[0x7c00+number+0x01] 86 | add al,0x30 87 | mov [es:0x20],al 88 | mov byte [es:0x21],0x04 89 | 90 | mov al,[0x7c00+number+0x00] 91 | add al,0x30 92 | mov [es:0x22],al 93 | mov byte [es:0x23],0x04 94 | 95 | mov byte [es:0x24],'D' 96 | mov byte [es:0x25],0x07 97 | 98 | infi: jmp near infi ;无限循环 99 | 100 | number db 0,0,0,0,0 101 | 102 | times 203 db 0 103 | db 0x55,0xaa -------------------------------------------------------------------------------- /booktool/c05/c05_mbr.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c05/c05_mbr.bin -------------------------------------------------------------------------------- /booktool/c05/c05_mbr.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c05/c05_mbr.img -------------------------------------------------------------------------------- /booktool/c05/c05_mbr.lst: -------------------------------------------------------------------------------- 1 | 1 ;代码清单5-1 2 | 2 ;文件名:c05_mbr.asm 3 | 3 ;文件说明:硬盘主引导扇区代码 4 | 4 ;创建日期:2011-3-31 21:15 5 | 5 6 | 6 00000000 B800B8 mov ax,0xb800 ;指向文本模式的显示缓冲区 7 | 7 00000003 8EC0 mov es,ax 8 | 8 9 | 9 ;以下显示字符串"Label offset:" 10 | 10 00000005 26C60600004C mov byte [es:0x00],'L' 11 | 11 0000000B 26C606010007 mov byte [es:0x01],0x07 12 | 12 00000011 26C606020061 mov byte [es:0x02],'a' 13 | 13 00000017 26C606030007 mov byte [es:0x03],0x07 14 | 14 0000001D 26C606040062 mov byte [es:0x04],'b' 15 | 15 00000023 26C606050007 mov byte [es:0x05],0x07 16 | 16 00000029 26C606060065 mov byte [es:0x06],'e' 17 | 17 0000002F 26C606070007 mov byte [es:0x07],0x07 18 | 18 00000035 26C60608006C mov byte [es:0x08],'l' 19 | 19 0000003B 26C606090007 mov byte [es:0x09],0x07 20 | 20 00000041 26C6060A0020 mov byte [es:0x0a],' ' 21 | 21 00000047 26C6060B0007 mov byte [es:0x0b],0x07 22 | 22 0000004D 26C6060C006F mov byte [es:0x0c],"o" 23 | 23 00000053 26C6060D0007 mov byte [es:0x0d],0x07 24 | 24 00000059 26C6060E0066 mov byte [es:0x0e],'f' 25 | 25 0000005F 26C6060F0007 mov byte [es:0x0f],0x07 26 | 26 00000065 26C606100066 mov byte [es:0x10],'f' 27 | 27 0000006B 26C606110007 mov byte [es:0x11],0x07 28 | 28 00000071 26C606120073 mov byte [es:0x12],'s' 29 | 29 00000077 26C606130007 mov byte [es:0x13],0x07 30 | 30 0000007D 26C606140065 mov byte [es:0x14],'e' 31 | 31 00000083 26C606150007 mov byte [es:0x15],0x07 32 | 32 00000089 26C606160074 mov byte [es:0x16],'t' 33 | 33 0000008F 26C606170007 mov byte [es:0x17],0x07 34 | 34 00000095 26C60618003A mov byte [es:0x18],':' 35 | 35 0000009B 26C606190007 mov byte [es:0x19],0x07 36 | 36 37 | 37 000000A1 B8[2E01] mov ax,number ;取得标号number的偏移地址 38 | 38 000000A4 BB0A00 mov bx,10 39 | 39 40 | 40 ;设置数据段的基地址 41 | 41 000000A7 8CC9 mov cx,cs 42 | 42 000000A9 8ED9 mov ds,cx 43 | 43 44 | 44 ;求个位上的数字 45 | 45 000000AB BA0000 mov dx,0 46 | 46 000000AE F7F3 div bx 47 | 47 000000B0 8816[2E7D] mov [0x7c00+number+0x00],dl ;保存个位上的数字 48 | 48 49 | 49 ;求十位上的数字 50 | 50 000000B4 31D2 xor dx,dx 51 | 51 000000B6 F7F3 div bx 52 | 52 000000B8 8816[2F7D] mov [0x7c00+number+0x01],dl ;保存十位上的数字 53 | 53 54 | 54 ;求百位上的数字 55 | 55 000000BC 31D2 xor dx,dx 56 | 56 000000BE F7F3 div bx 57 | 57 000000C0 8816[307D] mov [0x7c00+number+0x02],dl ;保存百位上的数字 58 | 58 59 | 59 ;求千位上的数字 60 | 60 000000C4 31D2 xor dx,dx 61 | 61 000000C6 F7F3 div bx 62 | 62 000000C8 8816[317D] mov [0x7c00+number+0x03],dl ;保存千位上的数字 63 | 63 64 | 64 ;求万位上的数字 65 | 65 000000CC 31D2 xor dx,dx 66 | 66 000000CE F7F3 div bx 67 | 67 000000D0 8816[327D] mov [0x7c00+number+0x04],dl ;保存万位上的数字 68 | 68 69 | 69 ;以下用十进制显示标号的偏移地址 70 | 70 000000D4 A0[327D] mov al,[0x7c00+number+0x04] 71 | 71 000000D7 0430 add al,0x30 72 | 72 000000D9 26A21A00 mov [es:0x1a],al 73 | 73 000000DD 26C6061B0004 mov byte [es:0x1b],0x04 74 | 74 75 | 75 000000E3 A0[317D] mov al,[0x7c00+number+0x03] 76 | 76 000000E6 0430 add al,0x30 77 | 77 000000E8 26A21C00 mov [es:0x1c],al 78 | 78 000000EC 26C6061D0004 mov byte [es:0x1d],0x04 79 | 79 80 | 80 000000F2 A0[307D] mov al,[0x7c00+number+0x02] 81 | 81 000000F5 0430 add al,0x30 82 | 82 000000F7 26A21E00 mov [es:0x1e],al 83 | 83 000000FB 26C6061F0004 mov byte [es:0x1f],0x04 84 | 84 85 | 85 00000101 A0[2F7D] mov al,[0x7c00+number+0x01] 86 | 86 00000104 0430 add al,0x30 87 | 87 00000106 26A22000 mov [es:0x20],al 88 | 88 0000010A 26C606210004 mov byte [es:0x21],0x04 89 | 89 90 | 90 00000110 A0[2E7D] mov al,[0x7c00+number+0x00] 91 | 91 00000113 0430 add al,0x30 92 | 92 00000115 26A22200 mov [es:0x22],al 93 | 93 00000119 26C606230004 mov byte [es:0x23],0x04 94 | 94 95 | 95 0000011F 26C606240044 mov byte [es:0x24],'D' 96 | 96 00000125 26C606250007 mov byte [es:0x25],0x07 97 | 97 98 | 98 0000012B E9FDFF infi: jmp near infi ;无限循环 99 | 99 100 | 100 0000012E 0000000000 number db 0,0,0,0,0 101 | 101 102 | 102 00000133 00 times 203 db 0 103 | 103 000001FE 55AA db 0x55,0xaa 104 | -------------------------------------------------------------------------------- /booktool/c06/c06_mbr.asm: -------------------------------------------------------------------------------- 1 | ;代码清单6-1 2 | ;文件名:c06_mbr.asm 3 | ;文件说明:硬盘主引导扇区代码 4 | ;创建日期:2011-4-12 22:12 5 | 6 | jmp near start 7 | 8 | mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\ 9 | 'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07 10 | number db 0,0,0,0,0 11 | 12 | start: 13 | mov ax,0x7c0 ;设置数据段基地址 14 | mov ds,ax 15 | 16 | mov ax,0xb800 ;设置附加段基地址 17 | mov es,ax 18 | 19 | cld 20 | mov si,mytext 21 | mov di,0 22 | mov cx,(number-mytext)/2 ;实际上等于 13 23 | rep movsw 24 | 25 | ;得到标号所代表的偏移地址 26 | mov ax,number 27 | 28 | ;计算各个数位 29 | mov bx,ax 30 | mov cx,5 ;循环次数 31 | mov si,10 ;除数 32 | digit: 33 | xor dx,dx 34 | div si 35 | mov [bx],dl ;保存数位 36 | inc bx 37 | loop digit 38 | 39 | ;显示各个数位 40 | mov bx,number 41 | mov si,4 42 | show: 43 | mov al,[bx+si] 44 | add al,0x30 45 | mov ah,0x04 46 | mov [es:di],ax 47 | add di,2 48 | dec si 49 | jns show 50 | 51 | mov word [es:di],0x0744 52 | 53 | jmp near $ 54 | 55 | times 510-($-$$) db 0 56 | db 0x55,0xaa -------------------------------------------------------------------------------- /booktool/c06/c06_mbr.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c06/c06_mbr.bin -------------------------------------------------------------------------------- /booktool/c06/c06_mbr.lst: -------------------------------------------------------------------------------- 1 | 1 ;代码清单6-1 2 | 2 ;文件名:c06_mbr.asm 3 | 3 ;文件说明:硬盘主引导扇区代码 4 | 4 ;创建日期:2011-4-12 22:12 5 | 5 6 | 6 00000000 E91F00 jmp near start 7 | 7 8 | 8 00000003 4C076107620765076C- 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 9 | 9 0000000C 0720076F0766076607- 10 | 10 00000015 7307650774073A07 11 | 11 0000001D 0000000000 number db 0,0,0,0,0 12 | 12 13 | 13 start: 14 | 14 00000022 B8C007 mov ax,0x7c0 ;设置数据段基地址 15 | 15 00000025 8ED8 mov ds,ax 16 | 16 17 | 17 00000027 B800B8 mov ax,0xb800 ;设置附加段基地址 18 | 18 0000002A 8EC0 mov es,ax 19 | 19 20 | 20 0000002C FC cld 21 | 21 0000002D BE[0300] mov si,mytext 22 | 22 00000030 BF0000 mov di,0 23 | 23 00000033 B90D00 mov cx,(number-mytext)/2 ;实际上等于 13 24 | 24 00000036 F3A5 rep movsw 25 | 25 26 | 26 ;得到标号所代表的偏移地址 27 | 27 00000038 B8[1D00] mov ax,number 28 | 28 29 | 29 ;计算各个数位 30 | 30 0000003B 89C3 mov bx,ax 31 | 31 0000003D B90500 mov cx,5 ;循环次数 32 | 32 00000040 BE0A00 mov si,10 ;除数 33 | 33 digit: 34 | 34 00000043 31D2 xor dx,dx 35 | 35 00000045 F7F6 div si 36 | 36 00000047 8817 mov [bx],dl ;保存数位 37 | 37 00000049 43 inc bx 38 | 38 0000004A E2F7 loop digit 39 | 39 40 | 40 ;显示各个数位 41 | 41 0000004C BB[1D00] mov bx,number 42 | 42 0000004F BE0400 mov si,4 43 | 43 show: 44 | 44 00000052 8A00 mov al,[bx+si] 45 | 45 00000054 0430 add al,0x30 46 | 46 00000056 B404 mov ah,0x04 47 | 47 00000058 268905 mov [es:di],ax 48 | 48 0000005B 83C702 add di,2 49 | 49 0000005E 4E dec si 50 | 50 0000005F 79F1 jns show 51 | 51 52 | 52 00000061 26C7054407 mov word [es:di],0x0744 53 | 53 54 | 54 00000066 E9FDFF jmp near $ 55 | 55 56 | 56 00000069 00 times 510-($-$$) db 0 57 | 57 000001FE 55AA db 0x55,0xaa 58 | -------------------------------------------------------------------------------- /booktool/c07/c07_mbr.asm: -------------------------------------------------------------------------------- 1 | ;代码清单7-1 2 | ;文件名:c07_mbr.asm 3 | ;文件说明:硬盘主引导扇区代码 4 | ;创建日期:2011-4-13 18:02 5 | 6 | jmp near start 7 | 8 | message db '1+2+3+...+100=' 9 | 10 | start: 11 | mov ax,0x7c0 ;设置数据段的段基地址 12 | mov ds,ax 13 | 14 | mov ax,0xb800 ;设置附加段基址到显示缓冲区 15 | mov es,ax 16 | 17 | ;以下显示字符串 18 | mov si,message 19 | mov di,0 20 | mov cx,start-message 21 | @g: 22 | mov al,[si] 23 | mov [es:di],al 24 | inc di 25 | mov byte [es:di],0x07 26 | inc di 27 | inc si 28 | loop @g 29 | 30 | ;以下计算1到100的和 31 | xor ax,ax 32 | mov cx,1 33 | @f: 34 | add ax,cx 35 | inc cx 36 | cmp cx,100 37 | jle @f 38 | 39 | ;以下计算累加和的每个数位 40 | xor cx,cx ;设置堆栈段的段基地址 41 | mov ss,cx 42 | mov sp,cx 43 | 44 | mov bx,10 45 | xor cx,cx 46 | @d: 47 | inc cx 48 | xor dx,dx 49 | div bx 50 | or dl,0x30 51 | push dx 52 | cmp ax,0 53 | jne @d 54 | 55 | ;以下显示各个数位 56 | @a: 57 | pop dx 58 | mov [es:di],dl 59 | inc di 60 | mov byte [es:di],0x07 61 | inc di 62 | loop @a 63 | 64 | jmp near $ 65 | 66 | 67 | times 510-($-$$) db 0 68 | db 0x55,0xaa -------------------------------------------------------------------------------- /booktool/c07/c07_mbr.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c07/c07_mbr.bin -------------------------------------------------------------------------------- /booktool/c07/c07_mbr.lst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c07/c07_mbr.lst -------------------------------------------------------------------------------- /booktool/c08/c08.asm: -------------------------------------------------------------------------------- 1 | ;代码清单8-2 2 | ;文件名:c08.asm 3 | ;文件说明:用户程序 4 | ;创建日期:2011-5-5 18:17 5 | 6 | ;=============================================================================== 7 | SECTION header vstart=0 ;定义用户程序头部段 8 | program_length dd program_end ;程序总长度[0x00] 9 | 10 | ;用户程序入口点 11 | code_entry dw start ;偏移地址[0x04] 12 | dd section.code_1.start ;段地址[0x06] 13 | 14 | realloc_tbl_len dw (header_end-code_1_segment)/4 15 | ;段重定位表项个数[0x0a] 16 | 17 | ;段重定位表 18 | code_1_segment dd section.code_1.start ;[0x0c] 19 | code_2_segment dd section.code_2.start ;[0x10] 20 | data_1_segment dd section.data_1.start ;[0x14] 21 | data_2_segment dd section.data_2.start ;[0x18] 22 | 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 | mov cl,[bx] 31 | or cl,cl ;cl=0 ? 32 | jz .exit ;是的,返回主程序 33 | call put_char 34 | inc bx ;下一个字符 35 | jmp put_string 36 | 37 | .exit: 38 | ret 39 | 40 | ;------------------------------------------------------------------------------- 41 | put_char: ;显示一个字符 42 | ;输入:cl=字符ascii 43 | push ax 44 | push bx 45 | push cx 46 | push dx 47 | push ds 48 | push es 49 | 50 | ;以下取当前光标位置 51 | mov dx,0x3d4 52 | mov al,0x0e 53 | out dx,al 54 | mov dx,0x3d5 55 | in al,dx ;高8位 56 | mov ah,al 57 | 58 | mov dx,0x3d4 59 | mov al,0x0f 60 | out dx,al 61 | mov dx,0x3d5 62 | in al,dx ;低8位 63 | mov bx,ax ;BX=代表光标位置的16位数 64 | 65 | cmp cl,0x0d ;回车符? 66 | jnz .put_0a ;不是。看看是不是换行等字符 67 | mov ax,bx ;此句略显多余,但去掉后还得改书,麻烦 68 | mov bl,80 69 | div bl 70 | mul bl 71 | mov bx,ax 72 | jmp .set_cursor 73 | 74 | .put_0a: 75 | cmp cl,0x0a ;换行符? 76 | jnz .put_other ;不是,那就正常显示字符 77 | add bx,80 78 | jmp .roll_screen 79 | 80 | .put_other: ;正常显示字符 81 | mov ax,0xb800 82 | mov es,ax 83 | shl bx,1 84 | mov [es:bx],cl 85 | 86 | ;以下将光标位置推进一个字符 87 | shr bx,1 88 | add bx,1 89 | 90 | .roll_screen: 91 | cmp bx,2000 ;光标超出屏幕?滚屏 92 | jl .set_cursor 93 | 94 | mov ax,0xb800 95 | mov ds,ax 96 | mov es,ax 97 | cld 98 | mov si,0xa0 99 | mov di,0x00 100 | mov cx,1920 101 | rep movsw 102 | mov bx,3840 ;清除屏幕最底一行 103 | mov cx,80 104 | .cls: 105 | mov word[es:bx],0x0720 106 | add bx,2 107 | loop .cls 108 | 109 | mov bx,1920 110 | 111 | .set_cursor: 112 | mov dx,0x3d4 113 | mov al,0x0e 114 | out dx,al 115 | mov dx,0x3d5 116 | mov al,bh 117 | out dx,al 118 | mov dx,0x3d4 119 | mov al,0x0f 120 | out dx,al 121 | mov dx,0x3d5 122 | mov al,bl 123 | out dx,al 124 | 125 | pop es 126 | pop ds 127 | pop dx 128 | pop cx 129 | pop bx 130 | pop ax 131 | 132 | ret 133 | 134 | ;------------------------------------------------------------------------------- 135 | start: 136 | ;初始执行时,DS和ES指向用户程序头部段 137 | mov ax,[stack_segment] ;设置到用户程序自己的堆栈 138 | mov ss,ax 139 | mov sp,stack_end 140 | 141 | mov ax,[data_1_segment] ;设置到用户程序自己的数据段 142 | mov ds,ax 143 | 144 | mov bx,msg0 145 | call put_string ;显示第一段信息 146 | 147 | push word [es:code_2_segment] 148 | mov ax,begin 149 | push ax ;可以直接push begin,80386+ 150 | 151 | retf ;转移到代码段2执行 152 | 153 | continue: 154 | mov ax,[es:data_2_segment] ;段寄存器DS切换到数据段2 155 | mov ds,ax 156 | 157 | mov bx,msg1 158 | call put_string ;显示第二段信息 159 | 160 | jmp $ 161 | 162 | ;=============================================================================== 163 | SECTION code_2 align=16 vstart=0 ;定义代码段2(16字节对齐) 164 | 165 | begin: 166 | push word [es:code_1_segment] 167 | mov ax,continue 168 | push ax ;可以直接push continue,80386+ 169 | 170 | retf ;转移到代码段1接着执行 171 | 172 | ;=============================================================================== 173 | SECTION data_1 align=16 vstart=0 174 | 175 | msg0 db ' This is NASM - the famous Netwide Assembler. ' 176 | db 'Back at SourceForge and in intensive development! ' 177 | db 'Get the current versions from http://www.nasm.us/.' 178 | db 0x0d,0x0a,0x0d,0x0a 179 | db ' Example code for calculate 1+2+...+1000:',0x0d,0x0a,0x0d,0x0a 180 | db ' xor dx,dx',0x0d,0x0a 181 | db ' xor ax,ax',0x0d,0x0a 182 | db ' xor cx,cx',0x0d,0x0a 183 | db ' @@:',0x0d,0x0a 184 | db ' inc cx',0x0d,0x0a 185 | db ' add ax,cx',0x0d,0x0a 186 | db ' adc dx,0',0x0d,0x0a 187 | db ' inc cx',0x0d,0x0a 188 | db ' cmp cx,1000',0x0d,0x0a 189 | db ' jle @@',0x0d,0x0a 190 | db ' ... ...(Some other codes)',0x0d,0x0a,0x0d,0x0a 191 | db 0 192 | 193 | ;=============================================================================== 194 | SECTION data_2 align=16 vstart=0 195 | 196 | msg1 db ' The above contents is written by LeeChung. ' 197 | db '2011-05-06' 198 | db 0 199 | 200 | ;=============================================================================== 201 | SECTION stack align=16 vstart=0 202 | 203 | resb 256 204 | 205 | stack_end: 206 | 207 | ;=============================================================================== 208 | SECTION trail align=16 209 | program_end: -------------------------------------------------------------------------------- /booktool/c08/c08.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c08/c08.bin -------------------------------------------------------------------------------- /booktool/c08/c08.lst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c08/c08.lst -------------------------------------------------------------------------------- /booktool/c08/c08_mbr.asm: -------------------------------------------------------------------------------- 1 | ;代码清单8-1 2 | ;文件名:c08_mbr.asm 3 | ;文件说明:硬盘主引导扇区代码(加载程序) 4 | ;创建日期:2011-5-5 18:17 5 | 6 | app_lba_start equ 100 ;声明常数(用户程序起始逻辑扇区号) 7 | ;常数的声明不会占用汇编地址 8 | 9 | SECTION mbr align=16 vstart=0x7c00 10 | 11 | ;设置堆栈段和栈指针 12 | mov ax,0 13 | mov ss,ax 14 | mov sp,ax 15 | 16 | mov ax,[cs:phy_base] ;计算用于加载用户程序的逻辑段地址 17 | mov dx,[cs:phy_base+0x02] 18 | mov bx,16 19 | div bx 20 | mov ds,ax ;令DS和ES指向该段以进行操作 21 | mov es,ax 22 | 23 | ;以下读取程序的起始部分 24 | xor di,di 25 | mov si,app_lba_start ;程序在硬盘上的起始逻辑扇区号 26 | xor bx,bx ;加载到DS:0x0000处 27 | call read_hard_disk_0 28 | 29 | ;以下判断整个程序有多大 30 | mov dx,[2] ;曾经把dx写成了ds,花了二十分钟排错 31 | mov ax,[0] 32 | mov bx,512 ;512字节每扇区 33 | div bx 34 | cmp dx,0 35 | jnz @1 ;未除尽,因此结果比实际扇区数少1 36 | dec ax ;已经读了一个扇区,扇区总数减1 37 | @1: 38 | cmp ax,0 ;考虑实际长度小于等于512个字节的情况 39 | jz direct 40 | 41 | ;读取剩余的扇区 42 | push ds ;以下要用到并改变DS寄存器 43 | 44 | mov cx,ax ;循环次数(剩余扇区数) 45 | @2: 46 | mov ax,ds 47 | add ax,0x20 ;得到下一个以512字节为边界的段地址 48 | mov ds,ax 49 | 50 | xor bx,bx ;每次读时,偏移地址始终为0x0000 51 | inc si ;下一个逻辑扇区 52 | call read_hard_disk_0 53 | loop @2 ;循环读,直到读完整个功能程序 54 | 55 | pop ds ;恢复数据段基址到用户程序头部段 56 | 57 | ;计算入口点代码段基址 58 | direct: 59 | mov dx,[0x08] 60 | mov ax,[0x06] 61 | call calc_segment_base 62 | mov [0x06],ax ;回填修正后的入口点代码段基址 63 | 64 | ;开始处理段重定位表 65 | mov cx,[0x0a] ;需要重定位的项目数量 66 | mov bx,0x0c ;重定位表首地址 67 | 68 | realloc: 69 | mov dx,[bx+0x02] ;32位地址的高16位 70 | mov ax,[bx] 71 | call calc_segment_base 72 | mov [bx],ax ;回填段的基址 73 | add bx,4 ;下一个重定位项(每项占4个字节) 74 | loop realloc 75 | 76 | jmp far [0x04] ;转移到用户程序 77 | 78 | ;------------------------------------------------------------------------------- 79 | read_hard_disk_0: ;从硬盘读取一个逻辑扇区 80 | ;输入:DI:SI=起始逻辑扇区号 81 | ; DS:BX=目标缓冲区地址 82 | push ax 83 | push bx 84 | push cx 85 | push dx 86 | 87 | mov dx,0x1f2 88 | mov al,1 89 | out dx,al ;读取的扇区数 90 | 91 | inc dx ;0x1f3 92 | mov ax,si 93 | out dx,al ;LBA地址7~0 94 | 95 | inc dx ;0x1f4 96 | mov al,ah 97 | out dx,al ;LBA地址15~8 98 | 99 | inc dx ;0x1f5 100 | mov ax,di 101 | out dx,al ;LBA地址23~16 102 | 103 | inc dx ;0x1f6 104 | mov al,0xe0 ;LBA28模式,主盘 105 | or al,ah ;LBA地址27~24 106 | out dx,al 107 | 108 | inc dx ;0x1f7 109 | mov al,0x20 ;读命令 110 | out dx,al 111 | 112 | .waits: 113 | in al,dx 114 | and al,0x88 115 | cmp al,0x08 116 | jnz .waits ;不忙,且硬盘已准备好数据传输 117 | 118 | mov cx,256 ;总共要读取的字数 119 | mov dx,0x1f0 120 | .readw: 121 | in ax,dx 122 | mov [bx],ax 123 | add bx,2 124 | loop .readw 125 | 126 | pop dx 127 | pop cx 128 | pop bx 129 | pop ax 130 | 131 | ret 132 | 133 | ;------------------------------------------------------------------------------- 134 | calc_segment_base: ;计算16位段地址 135 | ;输入:DX:AX=32位物理地址 136 | ;返回:AX=16位段基地址 137 | push dx 138 | 139 | add ax,[cs:phy_base] 140 | adc dx,[cs:phy_base+0x02] 141 | shr ax,4 142 | ror dx,4 143 | and dx,0xf000 144 | or ax,dx 145 | 146 | pop dx 147 | 148 | ret 149 | 150 | ;------------------------------------------------------------------------------- 151 | phy_base dd 0x10000 ;用户程序被加载的物理起始地址 152 | 153 | times 510-($-$$) db 0 154 | db 0x55,0xaa -------------------------------------------------------------------------------- /booktool/c08/c08_mbr.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c08/c08_mbr.bin -------------------------------------------------------------------------------- /booktool/c08/c08_mbr.lst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c08/c08_mbr.lst -------------------------------------------------------------------------------- /booktool/c09/c09_1.asm: -------------------------------------------------------------------------------- 1 | ;代码清单9-1 2 | ;文件名:c09_1.asm 3 | ;文件说明:用户程序 4 | ;创建日期:2011-4-16 22:03 5 | 6 | ;=============================================================================== 7 | SECTION header vstart=0 ;定义用户程序头部段 8 | program_length dd program_end ;程序总长度[0x00] 9 | 10 | ;用户程序入口点 11 | code_entry dw start ;偏移地址[0x04] 12 | dd section.code.start ;段地址[0x06] 13 | 14 | realloc_tbl_len dw (header_end-realloc_begin)/4 15 | ;段重定位表项个数[0x0a] 16 | 17 | realloc_begin: 18 | ;段重定位表 19 | code_segment dd section.code.start ;[0x0c] 20 | data_segment dd section.data.start ;[0x14] 21 | stack_segment dd section.stack.start ;[0x1c] 22 | 23 | header_end: 24 | 25 | ;=============================================================================== 26 | SECTION code align=16 vstart=0 ;定义代码段(16字节对齐) 27 | new_int_0x70: 28 | push ax 29 | push bx 30 | push cx 31 | push dx 32 | push es 33 | 34 | .w0: 35 | mov al,0x0a ;阻断NMI。当然,通常是不必要的 36 | or al,0x80 37 | out 0x70,al 38 | in al,0x71 ;读寄存器A 39 | test al,0x80 ;测试第7位UIP 40 | jnz .w0 ;以上代码对于更新周期结束中断来说 41 | ;是不必要的 42 | xor al,al 43 | or al,0x80 44 | out 0x70,al 45 | in al,0x71 ;读RTC当前时间(秒) 46 | push ax 47 | 48 | mov al,2 49 | or al,0x80 50 | out 0x70,al 51 | in al,0x71 ;读RTC当前时间(分) 52 | push ax 53 | 54 | mov al,4 55 | or al,0x80 56 | out 0x70,al 57 | in al,0x71 ;读RTC当前时间(时) 58 | push ax 59 | 60 | mov al,0x0c ;寄存器C的索引。且开放NMI 61 | out 0x70,al 62 | in al,0x71 ;读一下RTC的寄存器C,否则只发生一次中断 63 | ;此处不考虑闹钟和周期性中断的情况 64 | mov ax,0xb800 65 | mov es,ax 66 | 67 | pop ax 68 | call bcd_to_ascii 69 | mov bx,12*160 + 36*2 ;从屏幕上的12行36列开始显示 70 | 71 | mov [es:bx],ah 72 | mov [es:bx+2],al ;显示两位小时数字 73 | 74 | mov al,':' 75 | mov [es:bx+4],al ;显示分隔符':' 76 | not byte [es:bx+5] ;反转显示属性 77 | 78 | pop ax 79 | call bcd_to_ascii 80 | mov [es:bx+6],ah 81 | mov [es:bx+8],al ;显示两位分钟数字 82 | 83 | mov al,':' 84 | mov [es:bx+10],al ;显示分隔符':' 85 | not byte [es:bx+11] ;反转显示属性 86 | 87 | pop ax 88 | call bcd_to_ascii 89 | mov [es:bx+12],ah 90 | mov [es:bx+14],al ;显示两位小时数字 91 | 92 | mov al,0x20 ;中断结束命令EOI 93 | out 0xa0,al ;向从片发送 94 | out 0x20,al ;向主片发送 95 | 96 | pop es 97 | pop dx 98 | pop cx 99 | pop bx 100 | pop ax 101 | 102 | iret 103 | 104 | ;------------------------------------------------------------------------------- 105 | bcd_to_ascii: ;BCD码转ASCII 106 | ;输入:AL=bcd码 107 | ;输出:AX=ascii 108 | mov ah,al ;分拆成两个数字 109 | and al,0x0f ;仅保留低4位 110 | add al,0x30 ;转换成ASCII 111 | 112 | shr ah,4 ;逻辑右移4位 113 | and ah,0x0f 114 | add ah,0x30 115 | 116 | ret 117 | 118 | ;------------------------------------------------------------------------------- 119 | start: 120 | mov ax,[stack_segment] 121 | mov ss,ax 122 | mov sp,ss_pointer 123 | mov ax,[data_segment] 124 | mov ds,ax 125 | 126 | mov bx,init_msg ;显示初始信息 127 | call put_string 128 | 129 | mov bx,inst_msg ;显示安装信息 130 | call put_string 131 | 132 | mov al,0x70 133 | mov bl,4 134 | mul bl ;计算0x70号中断在IVT中的偏移 135 | mov bx,ax 136 | 137 | cli ;防止改动期间发生新的0x70号中断 138 | 139 | push es 140 | mov ax,0x0000 141 | mov es,ax 142 | mov word [es:bx],new_int_0x70 ;偏移地址。 143 | 144 | mov word [es:bx+2],cs ;段地址 145 | pop es 146 | 147 | mov al,0x0b ;RTC寄存器B 148 | or al,0x80 ;阻断NMI 149 | out 0x70,al 150 | mov al,0x12 ;设置寄存器B,禁止周期性中断,开放更 151 | out 0x71,al ;新结束后中断,BCD码,24小时制 152 | 153 | mov al,0x0c 154 | out 0x70,al 155 | in al,0x71 ;读RTC寄存器C,复位未决的中断状态 156 | 157 | in al,0xa1 ;读8259从片的IMR寄存器 158 | and al,0xfe ;清除bit 0(此位连接RTC) 159 | out 0xa1,al ;写回此寄存器 160 | 161 | sti ;重新开放中断 162 | 163 | mov bx,done_msg ;显示安装完成信息 164 | call put_string 165 | 166 | mov bx,tips_msg ;显示提示信息 167 | call put_string 168 | 169 | mov cx,0xb800 170 | mov ds,cx 171 | mov byte [12*160 + 33*2],'@' ;屏幕第12行,35列 172 | 173 | .idle: 174 | hlt ;使CPU进入低功耗状态,直到用中断唤醒 175 | not byte [12*160 + 33*2+1] ;反转显示属性 176 | jmp .idle 177 | 178 | ;------------------------------------------------------------------------------- 179 | put_string: ;显示串(0结尾)。 180 | ;输入:DS:BX=串地址 181 | mov cl,[bx] 182 | or cl,cl ;cl=0 ? 183 | jz .exit ;是的,返回主程序 184 | call put_char 185 | inc bx ;下一个字符 186 | jmp put_string 187 | 188 | .exit: 189 | ret 190 | 191 | ;------------------------------------------------------------------------------- 192 | put_char: ;显示一个字符 193 | ;输入:cl=字符ascii 194 | push ax 195 | push bx 196 | push cx 197 | push dx 198 | push ds 199 | push es 200 | 201 | ;以下取当前光标位置 202 | mov dx,0x3d4 203 | mov al,0x0e 204 | out dx,al 205 | mov dx,0x3d5 206 | in al,dx ;高8位 207 | mov ah,al 208 | 209 | mov dx,0x3d4 210 | mov al,0x0f 211 | out dx,al 212 | mov dx,0x3d5 213 | in al,dx ;低8位 214 | mov bx,ax ;BX=代表光标位置的16位数 215 | 216 | cmp cl,0x0d ;回车符? 217 | jnz .put_0a ;不是。看看是不是换行等字符 218 | mov ax,bx ; 219 | mov bl,80 220 | div bl 221 | mul bl 222 | mov bx,ax 223 | jmp .set_cursor 224 | 225 | .put_0a: 226 | cmp cl,0x0a ;换行符? 227 | jnz .put_other ;不是,那就正常显示字符 228 | add bx,80 229 | jmp .roll_screen 230 | 231 | .put_other: ;正常显示字符 232 | mov ax,0xb800 233 | mov es,ax 234 | shl bx,1 235 | mov [es:bx],cl 236 | 237 | ;以下将光标位置推进一个字符 238 | shr bx,1 239 | add bx,1 240 | 241 | .roll_screen: 242 | cmp bx,2000 ;光标超出屏幕?滚屏 243 | jl .set_cursor 244 | 245 | mov ax,0xb800 246 | mov ds,ax 247 | mov es,ax 248 | cld 249 | mov si,0xa0 250 | mov di,0x00 251 | mov cx,1920 252 | rep movsw 253 | mov bx,3840 ;清除屏幕最底一行 254 | mov cx,80 255 | .cls: 256 | mov word[es:bx],0x0720 257 | add bx,2 258 | loop .cls 259 | 260 | mov bx,1920 261 | 262 | .set_cursor: 263 | mov dx,0x3d4 264 | mov al,0x0e 265 | out dx,al 266 | mov dx,0x3d5 267 | mov al,bh 268 | out dx,al 269 | mov dx,0x3d4 270 | mov al,0x0f 271 | out dx,al 272 | mov dx,0x3d5 273 | mov al,bl 274 | out dx,al 275 | 276 | pop es 277 | pop ds 278 | pop dx 279 | pop cx 280 | pop bx 281 | pop ax 282 | 283 | ret 284 | 285 | ;=============================================================================== 286 | SECTION data align=16 vstart=0 287 | 288 | init_msg db 'Starting...',0x0d,0x0a,0 289 | 290 | inst_msg db 'Installing a new interrupt 70H...',0 291 | 292 | done_msg db 'Done.',0x0d,0x0a,0 293 | 294 | tips_msg db 'Clock is now working.',0 295 | 296 | ;=============================================================================== 297 | SECTION stack align=16 vstart=0 298 | 299 | resb 256 300 | ss_pointer: 301 | 302 | ;=============================================================================== 303 | SECTION program_trail 304 | program_end: -------------------------------------------------------------------------------- /booktool/c09/c09_1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c09/c09_1.bin -------------------------------------------------------------------------------- /booktool/c09/c09_1.lst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c09/c09_1.lst -------------------------------------------------------------------------------- /booktool/c09/c09_2.asm: -------------------------------------------------------------------------------- 1 | ;代码清单9-2 2 | ;文件名:c09_2.asm 3 | ;文件说明:用于演示BIOS中断的用户程序 4 | ;创建日期:2012-3-28 20:35 5 | 6 | ;=============================================================================== 7 | SECTION header vstart=0 ;定义用户程序头部段 8 | program_length dd program_end ;程序总长度[0x00] 9 | 10 | ;用户程序入口点 11 | code_entry dw start ;偏移地址[0x04] 12 | dd section.code.start ;段地址[0x06] 13 | 14 | realloc_tbl_len dw (header_end-realloc_begin)/4 15 | ;段重定位表项个数[0x0a] 16 | 17 | realloc_begin: 18 | ;段重定位表 19 | code_segment dd section.code.start ;[0x0c] 20 | data_segment dd section.data.start ;[0x14] 21 | stack_segment dd section.stack.start ;[0x1c] 22 | 23 | header_end: 24 | 25 | ;=============================================================================== 26 | SECTION code align=16 vstart=0 ;定义代码段(16字节对齐) 27 | start: 28 | mov ax,[stack_segment] 29 | mov ss,ax 30 | mov sp,ss_pointer 31 | mov ax,[data_segment] 32 | mov ds,ax 33 | 34 | mov cx,msg_end-message 35 | mov bx,message 36 | 37 | .putc: 38 | mov ah,0x0e 39 | mov al,[bx] 40 | int 0x10 41 | inc bx 42 | loop .putc 43 | 44 | .reps: 45 | mov ah,0x00 46 | int 0x16 47 | 48 | mov ah,0x0e 49 | mov bl,0x07 50 | int 0x10 51 | 52 | jmp .reps 53 | 54 | ;=============================================================================== 55 | SECTION data align=16 vstart=0 56 | 57 | message db 'Hello, friend!',0x0d,0x0a 58 | db 'This simple procedure used to demonstrate ' 59 | db 'the BIOS interrupt.',0x0d,0x0a 60 | db 'Please press the keys on the keyboard ->' 61 | msg_end: 62 | 63 | ;=============================================================================== 64 | SECTION stack align=16 vstart=0 65 | 66 | resb 256 67 | ss_pointer: 68 | 69 | ;=============================================================================== 70 | SECTION program_trail 71 | program_end: -------------------------------------------------------------------------------- /booktool/c09/c09_2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c09/c09_2.bin -------------------------------------------------------------------------------- /booktool/c09/c09_2.lst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/c09/c09_2.lst -------------------------------------------------------------------------------- /booktool/c11/c11_mbr.asm: -------------------------------------------------------------------------------- 1 | ;代码清单11-1 2 | ;文件名:c11_mbr.asm 3 | ;文件说明:硬盘主引导扇区代码 4 | ;创建日期:2011-5-16 19:54 5 | 6 | ;设置堆栈段和栈指针 7 | mov ax,cs 8 | mov ss,ax 9 | mov sp,0x7c00 10 | 11 | ;计算GDT所在的逻辑段地址 12 | mov ax,[cs:gdt_base+0x7c00] ;低16位 13 | mov dx,[cs:gdt_base+0x7c00+0x02] ;高16位 14 | mov bx,16 15 | div bx 16 | mov ds,ax ;令DS指向该段以进行操作 17 | mov bx,dx ;段内起始偏移地址 18 | 19 | ;创建0#描述符,它是空描述符,这是处理器的要求 20 | mov dword [bx+0x00],0x00 21 | mov dword [bx+0x04],0x00 22 | 23 | ;创建#1描述符,保护模式下的代码段描述符 24 | mov dword [bx+0x08],0x7c0001ff 25 | mov dword [bx+0x0c],0x00409800 26 | 27 | ;创建#2描述符,保护模式下的数据段描述符(文本模式下的显示缓冲区) 28 | mov dword [bx+0x10],0x8000ffff 29 | mov dword [bx+0x14],0x0040920b 30 | 31 | ;创建#3描述符,保护模式下的堆栈段描述符 32 | mov dword [bx+0x18],0x00007a00 33 | mov dword [bx+0x1c],0x00409600 34 | 35 | ;初始化描述符表寄存器GDTR 36 | mov word [cs: gdt_size+0x7c00],31 ;描述符表的界限(总字节数减一) 37 | 38 | lgdt [cs: gdt_size+0x7c00] 39 | 40 | in al,0x92 ;南桥芯片内的端口 41 | or al,0000_0010B 42 | out 0x92,al ;打开A20 43 | 44 | cli ;保护模式下中断机制尚未建立,应 45 | ;禁止中断 46 | mov eax,cr0 47 | or eax,1 48 | mov cr0,eax ;设置PE位 49 | 50 | ;以下进入保护模式... ... 51 | jmp dword 0x0008:flush ;16位的描述符选择子:32位偏移 52 | ;清流水线并串行化处理器 53 | [bits 32] 54 | 55 | flush: 56 | mov cx,00000000000_10_000B ;加载数据段选择子(0x10) 57 | mov ds,cx 58 | 59 | ;以下在屏幕上显示"Protect mode OK." 60 | mov byte [0x00],'P' 61 | mov byte [0x02],'r' 62 | mov byte [0x04],'o' 63 | mov byte [0x06],'t' 64 | mov byte [0x08],'e' 65 | mov byte [0x0a],'c' 66 | mov byte [0x0c],'t' 67 | mov byte [0x0e],' ' 68 | mov byte [0x10],'m' 69 | mov byte [0x12],'o' 70 | mov byte [0x14],'d' 71 | mov byte [0x16],'e' 72 | mov byte [0x18],' ' 73 | mov byte [0x1a],'O' 74 | mov byte [0x1c],'K' 75 | 76 | ;以下用简单的示例来帮助阐述32位保护模式下的堆栈操作 77 | mov cx,00000000000_11_000B ;加载堆栈段选择子 78 | mov ss,cx 79 | mov esp,0x7c00 80 | 81 | mov ebp,esp ;保存堆栈指针 82 | push byte '.' ;压入立即数(字节) 83 | 84 | sub ebp,4 85 | cmp ebp,esp ;判断压入立即数时,ESP是否减4 86 | jnz ghalt 87 | pop eax 88 | mov [0x1e],al ;显示句点 89 | 90 | ghalt: 91 | hlt ;已经禁止中断,将不会被唤醒 92 | 93 | ;------------------------------------------------------------------------------- 94 | 95 | gdt_size dw 0 96 | gdt_base dd 0x00007e00 ;GDT的物理地址 97 | 98 | times 510-($-$$) db 0 99 | db 0x55,0xaa -------------------------------------------------------------------------------- /booktool/c12/c12_mbr.asm: -------------------------------------------------------------------------------- 1 | ;代码清单12-1 2 | ;文件名:c12_mbr.asm 3 | ;文件说明:硬盘主引导扇区代码 4 | ;创建日期:2011-10-27 22:52 5 | 6 | ;设置堆栈段和栈指针 7 | mov eax,cs 8 | mov ss,eax 9 | mov sp,0x7c00 10 | 11 | ;计算GDT所在的逻辑段地址 12 | mov eax,[cs:pgdt+0x7c00+0x02] ;GDT的32位线性基地址 13 | xor edx,edx 14 | mov ebx,16 15 | div ebx ;分解成16位逻辑地址 16 | 17 | mov ds,eax ;令DS指向该段以进行操作 18 | mov ebx,edx ;段内起始偏移地址 19 | 20 | ;创建0#描述符,它是空描述符,这是处理器的要求 21 | mov dword [ebx+0x00],0x00000000 22 | mov dword [ebx+0x04],0x00000000 23 | 24 | ;创建1#描述符,这是一个数据段,对应0~4GB的线性地址空间 25 | mov dword [ebx+0x08],0x0000ffff ;基地址为0,段界限为0xfffff 26 | mov dword [ebx+0x0c],0x00cf9200 ;粒度为4KB,存储器段描述符 27 | 28 | ;创建保护模式下初始代码段描述符 29 | mov dword [ebx+0x10],0x7c0001ff ;基地址为0x00007c00,512字节 30 | mov dword [ebx+0x14],0x00409800 ;粒度为1个字节,代码段描述符 31 | 32 | ;创建以上代码段的别名描述符 33 | mov dword [ebx+0x18],0x7c0001ff ;基地址为0x00007c00,512字节 34 | mov dword [ebx+0x1c],0x00409200 ;粒度为1个字节,数据段描述符 35 | 36 | mov dword [ebx+0x20],0x7c00fffe 37 | mov dword [ebx+0x24],0x00cf9600 38 | 39 | ;初始化描述符表寄存器GDTR 40 | mov word [cs: pgdt+0x7c00],39 ;描述符表的界限 41 | 42 | lgdt [cs: pgdt+0x7c00] 43 | 44 | in al,0x92 ;南桥芯片内的端口 45 | or al,0000_0010B 46 | out 0x92,al ;打开A20 47 | 48 | cli ;中断机制尚未工作 49 | 50 | mov eax,cr0 51 | or eax,1 52 | mov cr0,eax ;设置PE位 53 | 54 | ;以下进入保护模式... ... 55 | jmp dword 0x0010:flush ;16位的描述符选择子:32位偏移 56 | 57 | [bits 32] 58 | flush: 59 | mov eax,0x0018 60 | mov ds,eax 61 | 62 | mov eax,0x0008 ;加载数据段(0..4GB)选择子 63 | mov es,eax 64 | mov fs,eax 65 | mov gs,eax 66 | 67 | mov eax,0x0020 ;0000 0000 0010 0000 68 | mov ss,eax 69 | xor esp,esp ;ESP <- 0 70 | 71 | mov dword [es:0x0b8000],0x072e0750 ;字符'P'、'.'及其显示属性 72 | mov dword [es:0x0b8004],0x072e074d ;字符'M'、'.'及其显示属性 73 | mov dword [es:0x0b8008],0x07200720 ;两个空白字符及其显示属性 74 | mov dword [es:0x0b800c],0x076b076f ;字符'o'、'k'及其显示属性 75 | 76 | ;开始冒泡排序 77 | mov ecx,pgdt-string-1 ;遍历次数=串长度-1 78 | @@1: 79 | push ecx ;32位模式下的loop使用ecx 80 | xor bx,bx ;32位模式下,偏移量可以是16位,也可以 81 | @@2: ;是后面的32位 82 | mov ax,[string+bx] 83 | cmp ah,al ;ah中存放的是源字的高字节 84 | jge @@3 85 | xchg al,ah 86 | mov [string+bx],ax 87 | @@3: 88 | inc bx 89 | loop @@2 90 | pop ecx 91 | loop @@1 92 | 93 | mov ecx,pgdt-string 94 | xor ebx,ebx ;偏移地址是32位的情况 95 | @@4: ;32位的偏移具有更大的灵活性 96 | mov ah,0x07 97 | mov al,[string+ebx] 98 | mov [es:0xb80a0+ebx*2],ax ;演示0~4GB寻址。 99 | inc ebx 100 | loop @@4 101 | 102 | hlt 103 | 104 | ;------------------------------------------------------------------------------- 105 | string db 's0ke4or92xap3fv8giuzjcy5l1m7hd6bnqtw.' 106 | ;------------------------------------------------------------------------------- 107 | pgdt dw 0 108 | dd 0x00007e00 ;GDT的物理地址 109 | ;------------------------------------------------------------------------------- 110 | times 510-($-$$) db 0 111 | db 0x55,0xaa -------------------------------------------------------------------------------- /booktool/c13/c13.asm: -------------------------------------------------------------------------------- 1 | ;代码清单13-3 2 | ;文件名:c13.asm 3 | ;文件说明:用户程序 4 | ;创建日期:2011-10-30 15:19 5 | 6 | ;=============================================================================== 7 | SECTION header vstart=0 8 | 9 | program_length dd program_end ;程序总长度#0x00 10 | 11 | head_len dd header_end ;程序头部的长度#0x04 12 | 13 | stack_seg dd 0 ;用于接收堆栈段选择子#0x08 14 | stack_len dd 1 ;程序建议的堆栈大小#0x0c 15 | ;以4KB为单位 16 | 17 | prgentry dd start ;程序入口#0x10 18 | code_seg dd section.code.start ;代码段位置#0x14 19 | code_len dd code_end ;代码段长度#0x18 20 | 21 | data_seg dd section.data.start ;数据段位置#0x1c 22 | data_len dd data_end ;数据段长度#0x20 23 | 24 | ;------------------------------------------------------------------------------- 25 | ;符号地址检索表 26 | salt_items dd (header_end-salt)/256 ;#0x24 27 | 28 | salt: ;#0x28 29 | PrintString db '@PrintString' 30 | times 256-($-PrintString) db 0 31 | 32 | TerminateProgram db '@TerminateProgram' 33 | times 256-($-TerminateProgram) db 0 34 | 35 | ReadDiskData db '@ReadDiskData' 36 | times 256-($-ReadDiskData) db 0 37 | 38 | header_end: 39 | 40 | ;=============================================================================== 41 | SECTION data vstart=0 42 | 43 | buffer times 1024 db 0 ;缓冲区 44 | 45 | message_1 db 0x0d,0x0a,0x0d,0x0a 46 | db '**********User program is runing**********' 47 | db 0x0d,0x0a,0 48 | message_2 db ' Disk data:',0x0d,0x0a,0 49 | 50 | data_end: 51 | 52 | ;=============================================================================== 53 | [bits 32] 54 | ;=============================================================================== 55 | SECTION code vstart=0 56 | start: 57 | mov eax,ds 58 | mov fs,eax 59 | 60 | mov eax,[stack_seg] 61 | mov ss,eax 62 | mov esp,0 63 | 64 | mov eax,[data_seg] 65 | mov ds,eax 66 | 67 | mov ebx,message_1 68 | call far [fs:PrintString] 69 | 70 | mov eax,100 ;逻辑扇区号100 71 | mov ebx,buffer ;缓冲区偏移地址 72 | call far [fs:ReadDiskData] ;段间调用 73 | 74 | mov ebx,message_2 75 | call far [fs:PrintString] 76 | 77 | mov ebx,buffer 78 | call far [fs:PrintString] ;too. 79 | 80 | jmp far [fs:TerminateProgram] ;将控制权返回到系统 81 | 82 | code_end: 83 | 84 | ;=============================================================================== 85 | SECTION trail 86 | ;------------------------------------------------------------------------------- 87 | program_end: -------------------------------------------------------------------------------- /booktool/c13/c13_core.asm: -------------------------------------------------------------------------------- 1 | ;代码清单13-2 2 | ;文件名:c13_core.asm 3 | ;文件说明:保护模式微型核心程序 4 | ;创建日期:2011-10-26 12:11 5 | 6 | ;以下常量定义部分。内核的大部分内容都应当固定 7 | core_code_seg_sel equ 0x38 ;内核代码段选择子 8 | core_data_seg_sel equ 0x30 ;内核数据段选择子 9 | sys_routine_seg_sel equ 0x28 ;系统公共例程代码段的选择子 10 | video_ram_seg_sel equ 0x20 ;视频显示缓冲区的段选择子 11 | core_stack_seg_sel equ 0x18 ;内核堆栈段选择子 12 | mem_0_4_gb_seg_sel equ 0x08 ;整个0-4GB内存的段的选择子 13 | 14 | ;------------------------------------------------------------------------------- 15 | ;以下是系统核心的头部,用于加载核心程序 16 | core_length dd core_end ;核心程序总长度#00 17 | 18 | sys_routine_seg dd section.sys_routine.start 19 | ;系统公用例程段位置#04 20 | 21 | core_data_seg dd section.core_data.start 22 | ;核心数据段位置#08 23 | 24 | core_code_seg dd section.core_code.start 25 | ;核心代码段位置#0c 26 | 27 | 28 | core_entry dd start ;核心代码段入口点#10 29 | dw core_code_seg_sel 30 | 31 | ;=============================================================================== 32 | [bits 32] 33 | ;=============================================================================== 34 | SECTION sys_routine vstart=0 ;系统公共例程代码段 35 | ;------------------------------------------------------------------------------- 36 | ;字符串显示例程 37 | put_string: ;显示0终止的字符串并移动光标 38 | ;输入:DS:EBX=串地址 39 | push ecx 40 | .getc: 41 | mov cl,[ebx] 42 | or cl,cl 43 | jz .exit 44 | call put_char 45 | inc ebx 46 | jmp .getc 47 | 48 | .exit: 49 | pop ecx 50 | retf ;段间返回 51 | 52 | ;------------------------------------------------------------------------------- 53 | put_char: ;在当前光标处显示一个字符,并推进 54 | ;光标。仅用于段内调用 55 | ;输入:CL=字符ASCII码 56 | pushad 57 | 58 | ;以下取当前光标位置 59 | mov dx,0x3d4 60 | mov al,0x0e 61 | out dx,al 62 | inc dx ;0x3d5 63 | in al,dx ;高字 64 | mov ah,al 65 | 66 | dec dx ;0x3d4 67 | mov al,0x0f 68 | out dx,al 69 | inc dx ;0x3d5 70 | in al,dx ;低字 71 | mov bx,ax ;BX=代表光标位置的16位数 72 | 73 | cmp cl,0x0d ;回车符? 74 | jnz .put_0a 75 | mov ax,bx 76 | mov bl,80 77 | div bl 78 | mul bl 79 | mov bx,ax 80 | jmp .set_cursor 81 | 82 | .put_0a: 83 | cmp cl,0x0a ;换行符? 84 | jnz .put_other 85 | add bx,80 86 | jmp .roll_screen 87 | 88 | .put_other: ;正常显示字符 89 | push es 90 | mov eax,video_ram_seg_sel ;0xb8000段的选择子 91 | mov es,eax 92 | shl bx,1 93 | mov [es:bx],cl 94 | pop es 95 | 96 | ;以下将光标位置推进一个字符 97 | shr bx,1 98 | inc bx 99 | 100 | .roll_screen: 101 | cmp bx,2000 ;光标超出屏幕?滚屏 102 | jl .set_cursor 103 | 104 | push ds 105 | push es 106 | mov eax,video_ram_seg_sel 107 | mov ds,eax 108 | mov es,eax 109 | cld 110 | mov esi,0xa0 ;小心!32位模式下movsb/w/d 111 | mov edi,0x00 ;使用的是esi/edi/ecx 112 | mov ecx,1920 113 | rep movsd 114 | mov bx,3840 ;清除屏幕最底一行 115 | mov ecx,80 ;32位程序应该使用ECX 116 | .cls: 117 | mov word[es:bx],0x0720 118 | add bx,2 119 | loop .cls 120 | 121 | pop es 122 | pop ds 123 | 124 | mov bx,1920 125 | 126 | .set_cursor: 127 | mov dx,0x3d4 128 | mov al,0x0e 129 | out dx,al 130 | inc dx ;0x3d5 131 | mov al,bh 132 | out dx,al 133 | dec dx ;0x3d4 134 | mov al,0x0f 135 | out dx,al 136 | inc dx ;0x3d5 137 | mov al,bl 138 | out dx,al 139 | 140 | popad 141 | ret 142 | 143 | ;------------------------------------------------------------------------------- 144 | read_hard_disk_0: ;从硬盘读取一个逻辑扇区 145 | ;EAX=逻辑扇区号 146 | ;DS:EBX=目标缓冲区地址 147 | ;返回:EBX=EBX+512 148 | push eax 149 | push ecx 150 | push edx 151 | 152 | push eax 153 | 154 | mov dx,0x1f2 155 | mov al,1 156 | out dx,al ;读取的扇区数 157 | 158 | inc dx ;0x1f3 159 | pop eax 160 | out dx,al ;LBA地址7~0 161 | 162 | inc dx ;0x1f4 163 | mov cl,8 164 | shr eax,cl 165 | out dx,al ;LBA地址15~8 166 | 167 | inc dx ;0x1f5 168 | shr eax,cl 169 | out dx,al ;LBA地址23~16 170 | 171 | inc dx ;0x1f6 172 | shr eax,cl 173 | or al,0xe0 ;第一硬盘 LBA地址27~24 174 | out dx,al 175 | 176 | inc dx ;0x1f7 177 | mov al,0x20 ;读命令 178 | out dx,al 179 | 180 | .waits: 181 | in al,dx 182 | and al,0x88 183 | cmp al,0x08 184 | jnz .waits ;不忙,且硬盘已准备好数据传输 185 | 186 | mov ecx,256 ;总共要读取的字数 187 | mov dx,0x1f0 188 | .readw: 189 | in ax,dx 190 | mov [ebx],ax 191 | add ebx,2 192 | loop .readw 193 | 194 | pop edx 195 | pop ecx 196 | pop eax 197 | 198 | retf ;段间返回 199 | 200 | ;------------------------------------------------------------------------------- 201 | ;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助 202 | put_hex_dword: ;在当前光标处以十六进制形式显示 203 | ;一个双字并推进光标 204 | ;输入:EDX=要转换并显示的数字 205 | ;输出:无 206 | pushad 207 | push ds 208 | 209 | mov ax,core_data_seg_sel ;切换到核心数据段 210 | mov ds,ax 211 | 212 | mov ebx,bin_hex ;指向核心数据段内的转换表 213 | mov ecx,8 214 | .xlt: 215 | rol edx,4 216 | mov eax,edx 217 | and eax,0x0000000f 218 | xlat 219 | 220 | push ecx 221 | mov cl,al 222 | call put_char 223 | pop ecx 224 | 225 | loop .xlt 226 | 227 | pop ds 228 | popad 229 | retf 230 | 231 | ;------------------------------------------------------------------------------- 232 | allocate_memory: ;分配内存 233 | ;输入:ECX=希望分配的字节数 234 | ;输出:ECX=起始线性地址 235 | push ds 236 | push eax 237 | push ebx 238 | 239 | mov eax,core_data_seg_sel 240 | mov ds,eax 241 | 242 | mov eax,[ram_alloc] 243 | add eax,ecx ;下一次分配时的起始地址 244 | 245 | ;这里应当有检测可用内存数量的指令 246 | 247 | mov ecx,[ram_alloc] ;返回分配的起始地址 248 | 249 | mov ebx,eax 250 | and ebx,0xfffffffc 251 | add ebx,4 ;强制对齐 252 | test eax,0x00000003 ;下次分配的起始地址最好是4字节对齐 253 | cmovnz eax,ebx ;如果没有对齐,则强制对齐 254 | mov [ram_alloc],eax ;下次从该地址分配内存 255 | ;cmovcc指令可以避免控制转移 256 | pop ebx 257 | pop eax 258 | pop ds 259 | 260 | retf 261 | 262 | ;------------------------------------------------------------------------------- 263 | set_up_gdt_descriptor: ;在GDT内安装一个新的描述符 264 | ;输入:EDX:EAX=描述符 265 | ;输出:CX=描述符的选择子 266 | push eax 267 | push ebx 268 | push edx 269 | 270 | push ds 271 | push es 272 | 273 | mov ebx,core_data_seg_sel ;切换到核心数据段 274 | mov ds,ebx 275 | 276 | sgdt [pgdt] ;以便开始处理GDT 277 | 278 | mov ebx,mem_0_4_gb_seg_sel 279 | mov es,ebx 280 | 281 | movzx ebx,word [pgdt] ;GDT界限 282 | inc bx ;GDT总字节数,也是下一个描述符偏移 283 | add ebx,[pgdt+2] ;下一个描述符的线性地址 284 | 285 | mov [es:ebx],eax 286 | mov [es:ebx+4],edx 287 | 288 | add word [pgdt],8 ;增加一个描述符的大小 289 | 290 | lgdt [pgdt] ;对GDT的更改生效 291 | 292 | mov ax,[pgdt] ;得到GDT界限值 293 | xor dx,dx 294 | mov bx,8 295 | div bx ;除以8,去掉余数 296 | mov cx,ax 297 | shl cx,3 ;将索引号移到正确位置 298 | 299 | pop es 300 | pop ds 301 | 302 | pop edx 303 | pop ebx 304 | pop eax 305 | 306 | retf 307 | ;------------------------------------------------------------------------------- 308 | make_seg_descriptor: ;构造存储器和系统的段描述符 309 | ;输入:EAX=线性基地址 310 | ; EBX=段界限 311 | ; ECX=属性。各属性位都在原始 312 | ; 位置,无关的位清零 313 | ;返回:EDX:EAX=描述符 314 | mov edx,eax 315 | shl eax,16 316 | or ax,bx ;描述符前32位(EAX)构造完毕 317 | 318 | and edx,0xffff0000 ;清除基地址中无关的位 319 | rol edx,8 320 | bswap edx ;装配基址的31~24和23~16 (80486+) 321 | 322 | xor bx,bx 323 | or edx,ebx ;装配段界限的高4位 324 | 325 | or edx,ecx ;装配属性 326 | 327 | retf 328 | 329 | ;=============================================================================== 330 | SECTION core_data vstart=0 ;系统核心的数据段 331 | ;------------------------------------------------------------------------------- 332 | pgdt dw 0 ;用于设置和修改GDT 333 | dd 0 334 | 335 | ram_alloc dd 0x00100000 ;下次分配内存时的起始地址 336 | 337 | ;符号地址检索表 338 | salt: 339 | salt_1 db '@PrintString' 340 | times 256-($-salt_1) db 0 341 | dd put_string 342 | dw sys_routine_seg_sel 343 | 344 | salt_2 db '@ReadDiskData' 345 | times 256-($-salt_2) db 0 346 | dd read_hard_disk_0 347 | dw sys_routine_seg_sel 348 | 349 | salt_3 db '@PrintDwordAsHexString' 350 | times 256-($-salt_3) db 0 351 | dd put_hex_dword 352 | dw sys_routine_seg_sel 353 | 354 | salt_4 db '@TerminateProgram' 355 | times 256-($-salt_4) db 0 356 | dd return_point 357 | dw core_code_seg_sel 358 | 359 | salt_item_len equ $-salt_4 360 | salt_items equ ($-salt)/salt_item_len 361 | 362 | message_1 db ' If you seen this message,that means we ' 363 | db 'are now in protect mode,and the system ' 364 | db 'core is loaded,and the video display ' 365 | db 'routine works perfectly.',0x0d,0x0a,0 366 | 367 | message_5 db ' Loading user program...',0 368 | 369 | do_status db 'Done.',0x0d,0x0a,0 370 | 371 | message_6 db 0x0d,0x0a,0x0d,0x0a,0x0d,0x0a 372 | db ' User program terminated,control returned.',0 373 | 374 | bin_hex db '0123456789ABCDEF' 375 | ;put_hex_dword子过程用的查找表 376 | core_buf times 2048 db 0 ;内核用的缓冲区 377 | 378 | esp_pointer dd 0 ;内核用来临时保存自己的栈指针 379 | 380 | cpu_brnd0 db 0x0d,0x0a,' ',0 381 | cpu_brand times 52 db 0 382 | cpu_brnd1 db 0x0d,0x0a,0x0d,0x0a,0 383 | 384 | ;=============================================================================== 385 | SECTION core_code vstart=0 386 | ;------------------------------------------------------------------------------- 387 | load_relocate_program: ;加载并重定位用户程序 388 | ;输入:ESI=起始逻辑扇区号 389 | ;返回:AX=指向用户程序头部的选择子 390 | push ebx 391 | push ecx 392 | push edx 393 | push esi 394 | push edi 395 | 396 | push ds 397 | push es 398 | 399 | mov eax,core_data_seg_sel 400 | mov ds,eax ;切换DS到内核数据段 401 | 402 | mov eax,esi ;读取程序头部数据 403 | mov ebx,core_buf 404 | call sys_routine_seg_sel:read_hard_disk_0 405 | 406 | ;以下判断整个程序有多大 407 | mov eax,[core_buf] ;程序尺寸 408 | mov ebx,eax 409 | and ebx,0xfffffe00 ;使之512字节对齐(能被512整除的数, 410 | add ebx,512 ;低9位都为0 411 | test eax,0x000001ff ;程序的大小正好是512的倍数吗? 412 | cmovnz eax,ebx ;不是。使用凑整的结果 413 | 414 | mov ecx,eax ;实际需要申请的内存数量 415 | call sys_routine_seg_sel:allocate_memory 416 | mov ebx,ecx ;ebx -> 申请到的内存首地址 417 | push ebx ;保存该首地址 418 | xor edx,edx 419 | mov ecx,512 420 | div ecx 421 | mov ecx,eax ;总扇区数 422 | 423 | mov eax,mem_0_4_gb_seg_sel ;切换DS到0-4GB的段 424 | mov ds,eax 425 | 426 | mov eax,esi ;起始扇区号 427 | .b1: 428 | call sys_routine_seg_sel:read_hard_disk_0 429 | inc eax 430 | loop .b1 ;循环读,直到读完整个用户程序 431 | 432 | ;建立程序头部段描述符 433 | pop edi ;恢复程序装载的首地址 434 | mov eax,edi ;程序头部起始线性地址 435 | mov ebx,[edi+0x04] ;段长度 436 | dec ebx ;段界限 437 | mov ecx,0x00409200 ;字节粒度的数据段描述符 438 | call sys_routine_seg_sel:make_seg_descriptor 439 | call sys_routine_seg_sel:set_up_gdt_descriptor 440 | mov [edi+0x04],cx 441 | 442 | ;建立程序代码段描述符 443 | mov eax,edi 444 | add eax,[edi+0x14] ;代码起始线性地址 445 | mov ebx,[edi+0x18] ;段长度 446 | dec ebx ;段界限 447 | mov ecx,0x00409800 ;字节粒度的代码段描述符 448 | call sys_routine_seg_sel:make_seg_descriptor 449 | call sys_routine_seg_sel:set_up_gdt_descriptor 450 | mov [edi+0x14],cx 451 | 452 | ;建立程序数据段描述符 453 | mov eax,edi 454 | add eax,[edi+0x1c] ;数据段起始线性地址 455 | mov ebx,[edi+0x20] ;段长度 456 | dec ebx ;段界限 457 | mov ecx,0x00409200 ;字节粒度的数据段描述符 458 | call sys_routine_seg_sel:make_seg_descriptor 459 | call sys_routine_seg_sel:set_up_gdt_descriptor 460 | mov [edi+0x1c],cx 461 | 462 | ;建立程序堆栈段描述符 463 | mov ecx,[edi+0x0c] ;4KB的倍率 464 | mov ebx,0x000fffff 465 | sub ebx,ecx ;得到段界限 466 | mov eax,4096 467 | mul dword [edi+0x0c] 468 | mov ecx,eax ;准备为堆栈分配内存 469 | call sys_routine_seg_sel:allocate_memory 470 | add eax,ecx ;得到堆栈的高端物理地址 471 | mov ecx,0x00c09600 ;4KB粒度的堆栈段描述符 472 | call sys_routine_seg_sel:make_seg_descriptor 473 | call sys_routine_seg_sel:set_up_gdt_descriptor 474 | mov [edi+0x08],cx 475 | 476 | ;重定位SALT 477 | mov eax,[edi+0x04] 478 | mov es,eax ;es -> 用户程序头部 479 | mov eax,core_data_seg_sel 480 | mov ds,eax 481 | 482 | cld 483 | 484 | mov ecx,[es:0x24] ;用户程序的SALT条目数 485 | mov edi,0x28 ;用户程序内的SALT位于头部内0x2c处 486 | .b2: 487 | push ecx 488 | push edi 489 | 490 | mov ecx,salt_items 491 | mov esi,salt 492 | .b3: 493 | push edi 494 | push esi 495 | push ecx 496 | 497 | mov ecx,64 ;检索表中,每条目的比较次数 498 | repe cmpsd ;每次比较4字节 499 | jnz .b4 500 | mov eax,[esi] ;若匹配,esi恰好指向其后的地址数据 501 | mov [es:edi-256],eax ;将字符串改写成偏移地址 502 | mov ax,[esi+4] 503 | mov [es:edi-252],ax ;以及段选择子 504 | .b4: 505 | 506 | pop ecx 507 | pop esi 508 | add esi,salt_item_len 509 | pop edi ;从头比较 510 | loop .b3 511 | 512 | pop edi 513 | add edi,256 514 | pop ecx 515 | loop .b2 516 | 517 | mov ax,[es:0x04] 518 | 519 | pop es ;恢复到调用此过程前的es段 520 | pop ds ;恢复到调用此过程前的ds段 521 | 522 | pop edi 523 | pop esi 524 | pop edx 525 | pop ecx 526 | pop ebx 527 | 528 | ret 529 | 530 | ;------------------------------------------------------------------------------- 531 | start: 532 | mov ecx,core_data_seg_sel ;使ds指向核心数据段 533 | mov ds,ecx 534 | 535 | mov ebx,message_1 536 | call sys_routine_seg_sel:put_string 537 | 538 | ;显示处理器品牌信息 539 | mov eax,0x80000002 540 | cpuid 541 | mov [cpu_brand + 0x00],eax 542 | mov [cpu_brand + 0x04],ebx 543 | mov [cpu_brand + 0x08],ecx 544 | mov [cpu_brand + 0x0c],edx 545 | 546 | mov eax,0x80000003 547 | cpuid 548 | mov [cpu_brand + 0x10],eax 549 | mov [cpu_brand + 0x14],ebx 550 | mov [cpu_brand + 0x18],ecx 551 | mov [cpu_brand + 0x1c],edx 552 | 553 | mov eax,0x80000004 554 | cpuid 555 | mov [cpu_brand + 0x20],eax 556 | mov [cpu_brand + 0x24],ebx 557 | mov [cpu_brand + 0x28],ecx 558 | mov [cpu_brand + 0x2c],edx 559 | 560 | mov ebx,cpu_brnd0 561 | call sys_routine_seg_sel:put_string 562 | mov ebx,cpu_brand 563 | call sys_routine_seg_sel:put_string 564 | mov ebx,cpu_brnd1 565 | call sys_routine_seg_sel:put_string 566 | 567 | mov ebx,message_5 568 | call sys_routine_seg_sel:put_string 569 | mov esi,50 ;用户程序位于逻辑50扇区 570 | call load_relocate_program 571 | 572 | mov ebx,do_status 573 | call sys_routine_seg_sel:put_string 574 | 575 | mov [esp_pointer],esp ;临时保存堆栈指针 576 | 577 | mov ds,ax 578 | 579 | jmp far [0x10] ;控制权交给用户程序(入口点) 580 | ;堆栈可能切换 581 | 582 | return_point: ;用户程序返回点 583 | mov eax,core_data_seg_sel ;使ds指向核心数据段 584 | mov ds,eax 585 | 586 | mov eax,core_stack_seg_sel ;切换回内核自己的堆栈 587 | mov ss,eax 588 | mov esp,[esp_pointer] 589 | 590 | mov ebx,message_6 591 | call sys_routine_seg_sel:put_string 592 | 593 | ;这里可以放置清除用户程序各种描述符的指令 594 | ;也可以加载并启动其它程序 595 | 596 | hlt 597 | 598 | ;=============================================================================== 599 | SECTION core_trail 600 | ;------------------------------------------------------------------------------- 601 | core_end: -------------------------------------------------------------------------------- /booktool/c13/c13_mbr.asm: -------------------------------------------------------------------------------- 1 | ;代码清单13-1 2 | ;文件名:c13_mbr.asm 3 | ;文件说明:硬盘主引导扇区代码 4 | ;创建日期:2011-10-28 22:35 ;设置堆栈段和栈指针 5 | 6 | core_base_address equ 0x00040000 ;常数,内核加载的起始内存地址 7 | core_start_sector equ 0x00000001 ;常数,内核的起始逻辑扇区号 8 | 9 | mov ax,cs 10 | mov ss,ax 11 | mov sp,0x7c00 12 | 13 | ;计算GDT所在的逻辑段地址 14 | mov eax,[cs:pgdt+0x7c00+0x02] ;GDT的32位物理地址 15 | xor edx,edx 16 | mov ebx,16 17 | div ebx ;分解成16位逻辑地址 18 | 19 | mov ds,eax ;令DS指向该段以进行操作 20 | mov ebx,edx ;段内起始偏移地址 21 | 22 | ;跳过0#号描述符的槽位 23 | ;创建1#描述符,这是一个数据段,对应0~4GB的线性地址空间 24 | mov dword [ebx+0x08],0x0000ffff ;基地址为0,段界限为0xFFFFF 25 | mov dword [ebx+0x0c],0x00cf9200 ;粒度为4KB,存储器段描述符 26 | 27 | ;创建保护模式下初始代码段描述符 28 | mov dword [ebx+0x10],0x7c0001ff ;基地址为0x00007c00,界限0x1FF 29 | mov dword [ebx+0x14],0x00409800 ;粒度为1个字节,代码段描述符 30 | 31 | ;建立保护模式下的堆栈段描述符 ;基地址为0x00007C00,界限0xFFFFE 32 | mov dword [ebx+0x18],0x7c00fffe ;粒度为4KB 33 | mov dword [ebx+0x1c],0x00cf9600 34 | 35 | ;建立保护模式下的显示缓冲区描述符 36 | mov dword [ebx+0x20],0x80007fff ;基地址为0x000B8000,界限0x07FFF 37 | mov dword [ebx+0x24],0x0040920b ;粒度为字节 38 | 39 | ;初始化描述符表寄存器GDTR 40 | mov word [cs: pgdt+0x7c00],39 ;描述符表的界限 41 | 42 | lgdt [cs: pgdt+0x7c00] 43 | 44 | in al,0x92 ;南桥芯片内的端口 45 | or al,0000_0010B 46 | out 0x92,al ;打开A20 47 | 48 | cli ;中断机制尚未工作 49 | 50 | mov eax,cr0 51 | or eax,1 52 | mov cr0,eax ;设置PE位 53 | 54 | ;以下进入保护模式... ... 55 | jmp dword 0x0010:flush ;16位的描述符选择子:32位偏移 56 | ;清流水线并串行化处理器 57 | [bits 32] 58 | flush: 59 | mov eax,0x0008 ;加载数据段(0..4GB)选择子 60 | mov ds,eax 61 | 62 | mov eax,0x0018 ;加载堆栈段选择子 63 | mov ss,eax 64 | xor esp,esp ;堆栈指针 <- 0 65 | 66 | ;以下加载系统核心程序 67 | mov edi,core_base_address 68 | 69 | mov eax,core_start_sector 70 | mov ebx,edi ;起始地址 71 | call read_hard_disk_0 ;以下读取程序的起始部分(一个扇区) 72 | 73 | ;以下判断整个程序有多大 74 | mov eax,[edi] ;核心程序尺寸 75 | xor edx,edx 76 | mov ecx,512 ;512字节每扇区 77 | div ecx 78 | 79 | or edx,edx 80 | jnz @1 ;未除尽,因此结果比实际扇区数少1 81 | dec eax ;已经读了一个扇区,扇区总数减1 82 | @1: 83 | or eax,eax ;考虑实际长度≤512个字节的情况 84 | jz setup ;EAX=0 ? 85 | 86 | ;读取剩余的扇区 87 | mov ecx,eax ;32位模式下的LOOP使用ECX 88 | mov eax,core_start_sector 89 | inc eax ;从下一个逻辑扇区接着读 90 | @2: 91 | call read_hard_disk_0 92 | inc eax 93 | loop @2 ;循环读,直到读完整个内核 94 | 95 | setup: 96 | mov esi,[0x7c00+pgdt+0x02] ;不可以在代码段内寻址pgdt,但可以 97 | ;通过4GB的段来访问 98 | ;建立公用例程段描述符 99 | mov eax,[edi+0x04] ;公用例程代码段起始汇编地址 100 | mov ebx,[edi+0x08] ;核心数据段汇编地址 101 | sub ebx,eax 102 | dec ebx ;公用例程段界限 103 | add eax,edi ;公用例程段基地址 104 | mov ecx,0x00409800 ;字节粒度的代码段描述符 105 | call make_gdt_descriptor 106 | mov [esi+0x28],eax 107 | mov [esi+0x2c],edx 108 | 109 | ;建立核心数据段描述符 110 | mov eax,[edi+0x08] ;核心数据段起始汇编地址 111 | mov ebx,[edi+0x0c] ;核心代码段汇编地址 112 | sub ebx,eax 113 | dec ebx ;核心数据段界限 114 | add eax,edi ;核心数据段基地址 115 | mov ecx,0x00409200 ;字节粒度的数据段描述符 116 | call make_gdt_descriptor 117 | mov [esi+0x30],eax 118 | mov [esi+0x34],edx 119 | 120 | ;建立核心代码段描述符 121 | mov eax,[edi+0x0c] ;核心代码段起始汇编地址 122 | mov ebx,[edi+0x00] ;程序总长度 123 | sub ebx,eax 124 | dec ebx ;核心代码段界限 125 | add eax,edi ;核心代码段基地址 126 | mov ecx,0x00409800 ;字节粒度的代码段描述符 127 | call make_gdt_descriptor 128 | mov [esi+0x38],eax 129 | mov [esi+0x3c],edx 130 | 131 | mov word [0x7c00+pgdt],63 ;描述符表的界限 132 | 133 | lgdt [0x7c00+pgdt] 134 | 135 | jmp far [edi+0x10] 136 | 137 | ;------------------------------------------------------------------------------- 138 | read_hard_disk_0: ;从硬盘读取一个逻辑扇区 139 | ;EAX=逻辑扇区号 140 | ;DS:EBX=目标缓冲区地址 141 | ;返回:EBX=EBX+512 142 | push eax 143 | push ecx 144 | push edx 145 | 146 | push eax 147 | 148 | mov dx,0x1f2 149 | mov al,1 150 | out dx,al ;读取的扇区数 151 | 152 | inc dx ;0x1f3 153 | pop eax 154 | out dx,al ;LBA地址7~0 155 | 156 | inc dx ;0x1f4 157 | mov cl,8 158 | shr eax,cl 159 | out dx,al ;LBA地址15~8 160 | 161 | inc dx ;0x1f5 162 | shr eax,cl 163 | out dx,al ;LBA地址23~16 164 | 165 | inc dx ;0x1f6 166 | shr eax,cl 167 | or al,0xe0 ;第一硬盘 LBA地址27~24 168 | out dx,al 169 | 170 | inc dx ;0x1f7 171 | mov al,0x20 ;读命令 172 | out dx,al 173 | 174 | .waits: 175 | in al,dx 176 | and al,0x88 177 | cmp al,0x08 178 | jnz .waits ;不忙,且硬盘已准备好数据传输 179 | 180 | mov ecx,256 ;总共要读取的字数 181 | mov dx,0x1f0 182 | .readw: 183 | in ax,dx 184 | mov [ebx],ax 185 | add ebx,2 186 | loop .readw 187 | 188 | pop edx 189 | pop ecx 190 | pop eax 191 | 192 | ret 193 | 194 | ;------------------------------------------------------------------------------- 195 | make_gdt_descriptor: ;构造描述符 196 | ;输入:EAX=线性基地址 197 | ; EBX=段界限 198 | ; ECX=属性(各属性位都在原始 199 | ; 位置,其它没用到的位置0) 200 | ;返回:EDX:EAX=完整的描述符 201 | mov edx,eax 202 | shl eax,16 203 | or ax,bx ;描述符前32位(EAX)构造完毕 204 | 205 | and edx,0xffff0000 ;清除基地址中无关的位 206 | rol edx,8 207 | bswap edx ;装配基址的31~24和23~16 (80486+) 208 | 209 | xor bx,bx 210 | or edx,ebx ;装配段界限的高4位 211 | 212 | or edx,ecx ;装配属性 213 | 214 | ret 215 | 216 | ;------------------------------------------------------------------------------- 217 | pgdt dw 0 218 | dd 0x00007e00 ;GDT的物理地址 219 | ;------------------------------------------------------------------------------- 220 | times 510-($-$$) db 0 221 | db 0x55,0xaa -------------------------------------------------------------------------------- /booktool/c13/diskdata.txt: -------------------------------------------------------------------------------- 1 | The Intel386 Processor (1985) 2 | The Intel386 processor was the first 32-bit processor in the IA-32 architecture family. It introduced 32-bit registers for use both to hold operands and for addressing. The lower half of each 32-bit Intel386 register retains the properties of the 16-bit registers of earlier generations, permitting backward compatibility. The processor also provides a virtual-8086 mode that allows for even greater efficiency when executing programs created for 8086/8088 processors. [END] -------------------------------------------------------------------------------- /booktool/c14/c14_core.asm: -------------------------------------------------------------------------------- 1 | ;代码清单14-1 2 | ;文件名:c14_core.asm 3 | ;文件说明:保护模式微型核心程序 4 | ;创建日期:2011-11-6 18:37 5 | 6 | ;以下常量定义部分。内核的大部分内容都应当固定 7 | core_code_seg_sel equ 0x38 ;内核代码段选择子 8 | core_data_seg_sel equ 0x30 ;内核数据段选择子 9 | sys_routine_seg_sel equ 0x28 ;系统公共例程代码段的选择子 10 | video_ram_seg_sel equ 0x20 ;视频显示缓冲区的段选择子 11 | core_stack_seg_sel equ 0x18 ;内核堆栈段选择子 12 | mem_0_4_gb_seg_sel equ 0x08 ;整个0-4GB内存的段的选择子 13 | 14 | ;------------------------------------------------------------------------------- 15 | ;以下是系统核心的头部,用于加载核心程序 16 | core_length dd core_end ;核心程序总长度#00 17 | 18 | sys_routine_seg dd section.sys_routine.start 19 | ;系统公用例程段位置#04 20 | 21 | core_data_seg dd section.core_data.start 22 | ;核心数据段位置#08 23 | 24 | core_code_seg dd section.core_code.start 25 | ;核心代码段位置#0c 26 | 27 | 28 | core_entry dd start ;核心代码段入口点#10 29 | dw core_code_seg_sel 30 | 31 | ;=============================================================================== 32 | [bits 32] 33 | ;=============================================================================== 34 | SECTION sys_routine vstart=0 ;系统公共例程代码段 35 | ;------------------------------------------------------------------------------- 36 | ;字符串显示例程 37 | put_string: ;显示0终止的字符串并移动光标 38 | ;输入:DS:EBX=串地址 39 | push ecx 40 | .getc: 41 | mov cl,[ebx] 42 | or cl,cl 43 | jz .exit 44 | call put_char 45 | inc ebx 46 | jmp .getc 47 | 48 | .exit: 49 | pop ecx 50 | retf ;段间返回 51 | 52 | ;------------------------------------------------------------------------------- 53 | put_char: ;在当前光标处显示一个字符,并推进 54 | ;光标。仅用于段内调用 55 | ;输入:CL=字符ASCII码 56 | pushad 57 | 58 | ;以下取当前光标位置 59 | mov dx,0x3d4 60 | mov al,0x0e 61 | out dx,al 62 | inc dx ;0x3d5 63 | in al,dx ;高字 64 | mov ah,al 65 | 66 | dec dx ;0x3d4 67 | mov al,0x0f 68 | out dx,al 69 | inc dx ;0x3d5 70 | in al,dx ;低字 71 | mov bx,ax ;BX=代表光标位置的16位数 72 | 73 | cmp cl,0x0d ;回车符? 74 | jnz .put_0a 75 | mov ax,bx 76 | mov bl,80 77 | div bl 78 | mul bl 79 | mov bx,ax 80 | jmp .set_cursor 81 | 82 | .put_0a: 83 | cmp cl,0x0a ;换行符? 84 | jnz .put_other 85 | add bx,80 86 | jmp .roll_screen 87 | 88 | .put_other: ;正常显示字符 89 | push es 90 | mov eax,video_ram_seg_sel ;0xb8000段的选择子 91 | mov es,eax 92 | shl bx,1 93 | mov [es:bx],cl 94 | pop es 95 | 96 | ;以下将光标位置推进一个字符 97 | shr bx,1 98 | inc bx 99 | 100 | .roll_screen: 101 | cmp bx,2000 ;光标超出屏幕?滚屏 102 | jl .set_cursor 103 | 104 | push ds 105 | push es 106 | mov eax,video_ram_seg_sel 107 | mov ds,eax 108 | mov es,eax 109 | cld 110 | mov esi,0xa0 ;小心!32位模式下movsb/w/d 111 | mov edi,0x00 ;使用的是esi/edi/ecx 112 | mov ecx,1920 113 | rep movsd 114 | mov bx,3840 ;清除屏幕最底一行 115 | mov ecx,80 ;32位程序应该使用ECX 116 | .cls: 117 | mov word[es:bx],0x0720 118 | add bx,2 119 | loop .cls 120 | 121 | pop es 122 | pop ds 123 | 124 | mov bx,1920 125 | 126 | .set_cursor: 127 | mov dx,0x3d4 128 | mov al,0x0e 129 | out dx,al 130 | inc dx ;0x3d5 131 | mov al,bh 132 | out dx,al 133 | dec dx ;0x3d4 134 | mov al,0x0f 135 | out dx,al 136 | inc dx ;0x3d5 137 | mov al,bl 138 | out dx,al 139 | 140 | popad 141 | 142 | ret 143 | 144 | ;------------------------------------------------------------------------------- 145 | read_hard_disk_0: ;从硬盘读取一个逻辑扇区 146 | ;EAX=逻辑扇区号 147 | ;DS:EBX=目标缓冲区地址 148 | ;返回:EBX=EBX+512 149 | push eax 150 | push ecx 151 | push edx 152 | 153 | push eax 154 | 155 | mov dx,0x1f2 156 | mov al,1 157 | out dx,al ;读取的扇区数 158 | 159 | inc dx ;0x1f3 160 | pop eax 161 | out dx,al ;LBA地址7~0 162 | 163 | inc dx ;0x1f4 164 | mov cl,8 165 | shr eax,cl 166 | out dx,al ;LBA地址15~8 167 | 168 | inc dx ;0x1f5 169 | shr eax,cl 170 | out dx,al ;LBA地址23~16 171 | 172 | inc dx ;0x1f6 173 | shr eax,cl 174 | or al,0xe0 ;第一硬盘 LBA地址27~24 175 | out dx,al 176 | 177 | inc dx ;0x1f7 178 | mov al,0x20 ;读命令 179 | out dx,al 180 | 181 | .waits: 182 | in al,dx 183 | and al,0x88 184 | cmp al,0x08 185 | jnz .waits ;不忙,且硬盘已准备好数据传输 186 | 187 | mov ecx,256 ;总共要读取的字数 188 | mov dx,0x1f0 189 | .readw: 190 | in ax,dx 191 | mov [ebx],ax 192 | add ebx,2 193 | loop .readw 194 | 195 | pop edx 196 | pop ecx 197 | pop eax 198 | 199 | retf ;段间返回 200 | 201 | ;------------------------------------------------------------------------------- 202 | ;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助 203 | put_hex_dword: ;在当前光标处以十六进制形式显示 204 | ;一个双字并推进光标 205 | ;输入:EDX=要转换并显示的数字 206 | ;输出:无 207 | pushad 208 | push ds 209 | 210 | mov ax,core_data_seg_sel ;切换到核心数据段 211 | mov ds,ax 212 | 213 | mov ebx,bin_hex ;指向核心数据段内的转换表 214 | mov ecx,8 215 | .xlt: 216 | rol edx,4 217 | mov eax,edx 218 | and eax,0x0000000f 219 | xlat 220 | 221 | push ecx 222 | mov cl,al 223 | call put_char 224 | pop ecx 225 | 226 | loop .xlt 227 | 228 | pop ds 229 | popad 230 | retf 231 | 232 | ;------------------------------------------------------------------------------- 233 | allocate_memory: ;分配内存 234 | ;输入:ECX=希望分配的字节数 235 | ;输出:ECX=起始线性地址 236 | push ds 237 | push eax 238 | push ebx 239 | 240 | mov eax,core_data_seg_sel 241 | mov ds,eax 242 | 243 | mov eax,[ram_alloc] 244 | add eax,ecx ;下一次分配时的起始地址 245 | 246 | ;这里应当有检测可用内存数量的指令 247 | 248 | mov ecx,[ram_alloc] ;返回分配的起始地址 249 | 250 | mov ebx,eax 251 | and ebx,0xfffffffc 252 | add ebx,4 ;强制对齐 253 | test eax,0x00000003 ;下次分配的起始地址最好是4字节对齐 254 | cmovnz eax,ebx ;如果没有对齐,则强制对齐 255 | mov [ram_alloc],eax ;下次从该地址分配内存 256 | ;cmovcc指令可以避免控制转移 257 | pop ebx 258 | pop eax 259 | pop ds 260 | 261 | retf 262 | 263 | ;------------------------------------------------------------------------------- 264 | set_up_gdt_descriptor: ;在GDT内安装一个新的描述符 265 | ;输入:EDX:EAX=描述符 266 | ;输出:CX=描述符的选择子 267 | push eax 268 | push ebx 269 | push edx 270 | 271 | push ds 272 | push es 273 | 274 | mov ebx,core_data_seg_sel ;切换到核心数据段 275 | mov ds,ebx 276 | 277 | sgdt [pgdt] ;以便开始处理GDT 278 | 279 | mov ebx,mem_0_4_gb_seg_sel 280 | mov es,ebx 281 | 282 | movzx ebx,word [pgdt] ;GDT界限 283 | inc bx ;GDT总字节数,也是下一个描述符偏移 284 | add ebx,[pgdt+2] ;下一个描述符的线性地址 285 | 286 | mov [es:ebx],eax 287 | mov [es:ebx+4],edx 288 | 289 | add word [pgdt],8 ;增加一个描述符的大小 290 | 291 | lgdt [pgdt] ;对GDT的更改生效 292 | 293 | mov ax,[pgdt] ;得到GDT界限值 294 | xor dx,dx 295 | mov bx,8 296 | div bx ;除以8,去掉余数 297 | mov cx,ax 298 | shl cx,3 ;将索引号移到正确位置 299 | 300 | pop es 301 | pop ds 302 | 303 | pop edx 304 | pop ebx 305 | pop eax 306 | 307 | retf 308 | ;------------------------------------------------------------------------------- 309 | make_seg_descriptor: ;构造存储器和系统的段描述符 310 | ;输入:EAX=线性基地址 311 | ; EBX=段界限 312 | ; ECX=属性。各属性位都在原始 313 | ; 位置,无关的位清零 314 | ;返回:EDX:EAX=描述符 315 | mov edx,eax 316 | shl eax,16 317 | or ax,bx ;描述符前32位(EAX)构造完毕 318 | 319 | and edx,0xffff0000 ;清除基地址中无关的位 320 | rol edx,8 321 | bswap edx ;装配基址的31~24和23~16 (80486+) 322 | 323 | xor bx,bx 324 | or edx,ebx ;装配段界限的高4位 325 | 326 | or edx,ecx ;装配属性 327 | 328 | retf 329 | 330 | ;------------------------------------------------------------------------------- 331 | make_gate_descriptor: ;构造门的描述符(调用门等) 332 | ;输入:EAX=门代码在段内偏移地址 333 | ; BX=门代码所在段的选择子 334 | ; CX=段类型及属性等(各属 335 | ; 性位都在原始位置) 336 | ;返回:EDX:EAX=完整的描述符 337 | push ebx 338 | push ecx 339 | 340 | mov edx,eax 341 | and edx,0xffff0000 ;得到偏移地址高16位 342 | or dx,cx ;组装属性部分到EDX 343 | 344 | and eax,0x0000ffff ;得到偏移地址低16位 345 | shl ebx,16 346 | or eax,ebx ;组装段选择子部分 347 | 348 | pop ecx 349 | pop ebx 350 | 351 | retf 352 | 353 | sys_routine_end: 354 | 355 | ;=============================================================================== 356 | SECTION core_data vstart=0 ;系统核心的数据段 357 | ;------------------------------------------------------------------------------- 358 | pgdt dw 0 ;用于设置和修改GDT 359 | dd 0 360 | 361 | ram_alloc dd 0x00100000 ;下次分配内存时的起始地址 362 | 363 | ;符号地址检索表 364 | salt: 365 | salt_1 db '@PrintString' 366 | times 256-($-salt_1) db 0 367 | dd put_string 368 | dw sys_routine_seg_sel 369 | 370 | salt_2 db '@ReadDiskData' 371 | times 256-($-salt_2) db 0 372 | dd read_hard_disk_0 373 | dw sys_routine_seg_sel 374 | 375 | salt_3 db '@PrintDwordAsHexString' 376 | times 256-($-salt_3) db 0 377 | dd put_hex_dword 378 | dw sys_routine_seg_sel 379 | 380 | salt_4 db '@TerminateProgram' 381 | times 256-($-salt_4) db 0 382 | dd return_point 383 | dw core_code_seg_sel 384 | 385 | salt_item_len equ $-salt_4 386 | salt_items equ ($-salt)/salt_item_len 387 | 388 | message_1 db ' If you seen this message,that means we ' 389 | db 'are now in protect mode,and the system ' 390 | db 'core is loaded,and the video display ' 391 | db 'routine works perfectly.',0x0d,0x0a,0 392 | 393 | message_2 db ' System wide CALL-GATE mounted.',0x0d,0x0a,0 394 | 395 | message_3 db 0x0d,0x0a,' Loading user program...',0 396 | 397 | do_status db 'Done.',0x0d,0x0a,0 398 | 399 | message_6 db 0x0d,0x0a,0x0d,0x0a,0x0d,0x0a 400 | db ' User program terminated,control returned.',0 401 | 402 | bin_hex db '0123456789ABCDEF' 403 | ;put_hex_dword子过程用的查找表 404 | 405 | core_buf times 2048 db 0 ;内核用的缓冲区 406 | 407 | esp_pointer dd 0 ;内核用来临时保存自己的栈指针 408 | 409 | cpu_brnd0 db 0x0d,0x0a,' ',0 410 | cpu_brand times 52 db 0 411 | cpu_brnd1 db 0x0d,0x0a,0x0d,0x0a,0 412 | 413 | ;任务控制块链 414 | tcb_chain dd 0 415 | 416 | core_data_end: 417 | 418 | ;=============================================================================== 419 | SECTION core_code vstart=0 420 | ;------------------------------------------------------------------------------- 421 | fill_descriptor_in_ldt: ;在LDT内安装一个新的描述符 422 | ;输入:EDX:EAX=描述符 423 | ; EBX=TCB基地址 424 | ;输出:CX=描述符的选择子 425 | push eax 426 | push edx 427 | push edi 428 | push ds 429 | 430 | mov ecx,mem_0_4_gb_seg_sel 431 | mov ds,ecx 432 | 433 | mov edi,[ebx+0x0c] ;获得LDT基地址 434 | 435 | xor ecx,ecx 436 | mov cx,[ebx+0x0a] ;获得LDT界限 437 | inc cx ;LDT的总字节数,即新描述符偏移地址 438 | 439 | mov [edi+ecx+0x00],eax 440 | mov [edi+ecx+0x04],edx ;安装描述符 441 | 442 | add cx,8 443 | dec cx ;得到新的LDT界限值 444 | 445 | mov [ebx+0x0a],cx ;更新LDT界限值到TCB 446 | 447 | mov ax,cx 448 | xor dx,dx 449 | mov cx,8 450 | div cx 451 | 452 | mov cx,ax 453 | shl cx,3 ;左移3位,并且 454 | or cx,0000_0000_0000_0100B ;使TI位=1,指向LDT,最后使RPL=00 455 | 456 | pop ds 457 | pop edi 458 | pop edx 459 | pop eax 460 | 461 | ret 462 | 463 | ;------------------------------------------------------------------------------- 464 | load_relocate_program: ;加载并重定位用户程序 465 | ;输入: PUSH 逻辑扇区号 466 | ; PUSH 任务控制块基地址 467 | ;输出:无 468 | pushad 469 | 470 | push ds 471 | push es 472 | 473 | mov ebp,esp ;为访问通过堆栈传递的参数做准备 474 | 475 | mov ecx,mem_0_4_gb_seg_sel 476 | mov es,ecx 477 | 478 | mov esi,[ebp+11*4] ;从堆栈中取得TCB的基地址 479 | 480 | ;以下申请创建LDT所需要的内存 481 | mov ecx,160 ;允许安装20个LDT描述符 482 | call sys_routine_seg_sel:allocate_memory 483 | mov [es:esi+0x0c],ecx ;登记LDT基地址到TCB中 484 | mov word [es:esi+0x0a],0xffff ;登记LDT初始的界限到TCB中 485 | 486 | ;以下开始加载用户程序 487 | mov eax,core_data_seg_sel 488 | mov ds,eax ;切换DS到内核数据段 489 | 490 | mov eax,[ebp+12*4] ;从堆栈中取出用户程序起始扇区号 491 | mov ebx,core_buf ;读取程序头部数据 492 | call sys_routine_seg_sel:read_hard_disk_0 493 | 494 | ;以下判断整个程序有多大 495 | mov eax,[core_buf] ;程序尺寸 496 | mov ebx,eax 497 | and ebx,0xfffffe00 ;使之512字节对齐(能被512整除的数低 498 | add ebx,512 ;9位都为0 499 | test eax,0x000001ff ;程序的大小正好是512的倍数吗? 500 | cmovnz eax,ebx ;不是。使用凑整的结果 501 | 502 | mov ecx,eax ;实际需要申请的内存数量 503 | call sys_routine_seg_sel:allocate_memory 504 | mov [es:esi+0x06],ecx ;登记程序加载基地址到TCB中 505 | 506 | mov ebx,ecx ;ebx -> 申请到的内存首地址 507 | xor edx,edx 508 | mov ecx,512 509 | div ecx 510 | mov ecx,eax ;总扇区数 511 | 512 | mov eax,mem_0_4_gb_seg_sel ;切换DS到0-4GB的段 513 | mov ds,eax 514 | 515 | mov eax,[ebp+12*4] ;起始扇区号 516 | .b1: 517 | call sys_routine_seg_sel:read_hard_disk_0 518 | inc eax 519 | loop .b1 ;循环读,直到读完整个用户程序 520 | 521 | mov edi,[es:esi+0x06] ;获得程序加载基地址 522 | 523 | ;建立程序头部段描述符 524 | mov eax,edi ;程序头部起始线性地址 525 | mov ebx,[edi+0x04] ;段长度 526 | dec ebx ;段界限 527 | mov ecx,0x0040f200 ;字节粒度的数据段描述符,特权级3 528 | call sys_routine_seg_sel:make_seg_descriptor 529 | 530 | ;安装头部段描述符到LDT中 531 | mov ebx,esi ;TCB的基地址 532 | call fill_descriptor_in_ldt 533 | 534 | or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 535 | mov [es:esi+0x44],cx ;登记程序头部段选择子到TCB 536 | mov [edi+0x04],cx ;和头部内 537 | 538 | ;建立程序代码段描述符 539 | mov eax,edi 540 | add eax,[edi+0x14] ;代码起始线性地址 541 | mov ebx,[edi+0x18] ;段长度 542 | dec ebx ;段界限 543 | mov ecx,0x0040f800 ;字节粒度的代码段描述符,特权级3 544 | call sys_routine_seg_sel:make_seg_descriptor 545 | mov ebx,esi ;TCB的基地址 546 | call fill_descriptor_in_ldt 547 | or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 548 | mov [edi+0x14],cx ;登记代码段选择子到头部 549 | 550 | ;建立程序数据段描述符 551 | mov eax,edi 552 | add eax,[edi+0x1c] ;数据段起始线性地址 553 | mov ebx,[edi+0x20] ;段长度 554 | dec ebx ;段界限 555 | mov ecx,0x0040f200 ;字节粒度的数据段描述符,特权级3 556 | call sys_routine_seg_sel:make_seg_descriptor 557 | mov ebx,esi ;TCB的基地址 558 | call fill_descriptor_in_ldt 559 | or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 560 | mov [edi+0x1c],cx ;登记数据段选择子到头部 561 | 562 | ;建立程序堆栈段描述符 563 | mov ecx,[edi+0x0c] ;4KB的倍率 564 | mov ebx,0x000fffff 565 | sub ebx,ecx ;得到段界限 566 | mov eax,4096 567 | mul ecx 568 | mov ecx,eax ;准备为堆栈分配内存 569 | call sys_routine_seg_sel:allocate_memory 570 | add eax,ecx ;得到堆栈的高端物理地址 571 | mov ecx,0x00c0f600 ;字节粒度的堆栈段描述符,特权级3 572 | call sys_routine_seg_sel:make_seg_descriptor 573 | mov ebx,esi ;TCB的基地址 574 | call fill_descriptor_in_ldt 575 | or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 576 | mov [edi+0x08],cx ;登记堆栈段选择子到头部 577 | 578 | ;重定位SALT 579 | mov eax,mem_0_4_gb_seg_sel ;这里和前一章不同,头部段描述符 580 | mov es,eax ;已安装,但还没有生效,故只能通 581 | ;过4GB段访问用户程序头部 582 | mov eax,core_data_seg_sel 583 | mov ds,eax 584 | 585 | cld 586 | 587 | mov ecx,[es:edi+0x24] ;U-SALT条目数(通过访问4GB段取得) 588 | add edi,0x28 ;U-SALT在4GB段内的偏移 589 | .b2: 590 | push ecx 591 | push edi 592 | 593 | mov ecx,salt_items 594 | mov esi,salt 595 | .b3: 596 | push edi 597 | push esi 598 | push ecx 599 | 600 | mov ecx,64 ;检索表中,每条目的比较次数 601 | repe cmpsd ;每次比较4字节 602 | jnz .b4 603 | mov eax,[esi] ;若匹配,则esi恰好指向其后的地址 604 | mov [es:edi-256],eax ;将字符串改写成偏移地址 605 | mov ax,[esi+4] 606 | or ax,0000000000000011B ;以用户程序自己的特权级使用调用门 607 | ;故RPL=3 608 | mov [es:edi-252],ax ;回填调用门选择子 609 | .b4: 610 | 611 | pop ecx 612 | pop esi 613 | add esi,salt_item_len 614 | pop edi ;从头比较 615 | loop .b3 616 | 617 | pop edi 618 | add edi,256 619 | pop ecx 620 | loop .b2 621 | 622 | mov esi,[ebp+11*4] ;从堆栈中取得TCB的基地址 623 | 624 | ;创建0特权级堆栈 625 | mov ecx,4096 626 | mov eax,ecx ;为生成堆栈高端地址做准备 627 | mov [es:esi+0x1a],ecx 628 | shr dword [es:esi+0x1a],12 ;登记0特权级堆栈尺寸到TCB 629 | call sys_routine_seg_sel:allocate_memory 630 | add eax,ecx ;堆栈必须使用高端地址为基地址 631 | mov [es:esi+0x1e],eax ;登记0特权级堆栈基地址到TCB 632 | mov ebx,0xffffe ;段长度(界限) 633 | mov ecx,0x00c09600 ;4KB粒度,读写,特权级0 634 | call sys_routine_seg_sel:make_seg_descriptor 635 | mov ebx,esi ;TCB的基地址 636 | call fill_descriptor_in_ldt 637 | ;or cx,0000_0000_0000_0000 ;设置选择子的特权级为0 638 | mov [es:esi+0x22],cx ;登记0特权级堆栈选择子到TCB 639 | mov dword [es:esi+0x24],0 ;登记0特权级堆栈初始ESP到TCB 640 | 641 | ;创建1特权级堆栈 642 | mov ecx,4096 643 | mov eax,ecx ;为生成堆栈高端地址做准备 644 | mov [es:esi+0x28],ecx 645 | shr [es:esi+0x28],12 ;登记1特权级堆栈尺寸到TCB 646 | call sys_routine_seg_sel:allocate_memory 647 | add eax,ecx ;堆栈必须使用高端地址为基地址 648 | mov [es:esi+0x2c],eax ;登记1特权级堆栈基地址到TCB 649 | mov ebx,0xffffe ;段长度(界限) 650 | mov ecx,0x00c0b600 ;4KB粒度,读写,特权级1 651 | call sys_routine_seg_sel:make_seg_descriptor 652 | mov ebx,esi ;TCB的基地址 653 | call fill_descriptor_in_ldt 654 | or cx,0000_0000_0000_0001 ;设置选择子的特权级为1 655 | mov [es:esi+0x30],cx ;登记1特权级堆栈选择子到TCB 656 | mov dword [es:esi+0x32],0 ;登记1特权级堆栈初始ESP到TCB 657 | 658 | ;创建2特权级堆栈 659 | mov ecx,4096 660 | mov eax,ecx ;为生成堆栈高端地址做准备 661 | mov [es:esi+0x36],ecx 662 | shr [es:esi+0x36],12 ;登记2特权级堆栈尺寸到TCB 663 | call sys_routine_seg_sel:allocate_memory 664 | add eax,ecx ;堆栈必须使用高端地址为基地址 665 | mov [es:esi+0x3a],ecx ;登记2特权级堆栈基地址到TCB 666 | mov ebx,0xffffe ;段长度(界限) 667 | mov ecx,0x00c0d600 ;4KB粒度,读写,特权级2 668 | call sys_routine_seg_sel:make_seg_descriptor 669 | mov ebx,esi ;TCB的基地址 670 | call fill_descriptor_in_ldt 671 | or cx,0000_0000_0000_0010 ;设置选择子的特权级为2 672 | mov [es:esi+0x3e],cx ;登记2特权级堆栈选择子到TCB 673 | mov dword [es:esi+0x40],0 ;登记2特权级堆栈初始ESP到TCB 674 | 675 | ;在GDT中登记LDT描述符 676 | mov eax,[es:esi+0x0c] ;LDT的起始线性地址 677 | movzx ebx,word [es:esi+0x0a] ;LDT段界限 678 | mov ecx,0x00408200 ;LDT描述符,特权级0 679 | call sys_routine_seg_sel:make_seg_descriptor 680 | call sys_routine_seg_sel:set_up_gdt_descriptor 681 | mov [es:esi+0x10],cx ;登记LDT选择子到TCB中 682 | 683 | ;创建用户程序的TSS 684 | mov ecx,104 ;tss的基本尺寸 685 | mov [es:esi+0x12],cx 686 | dec word [es:esi+0x12] ;登记TSS界限值到TCB 687 | call sys_routine_seg_sel:allocate_memory 688 | mov [es:esi+0x14],ecx ;登记TSS基地址到TCB 689 | 690 | ;登记基本的TSS表格内容 691 | mov word [es:ecx+0],0 ;反向链=0 692 | 693 | mov edx,[es:esi+0x24] ;登记0特权级堆栈初始ESP 694 | mov [es:ecx+4],edx ;到TSS中 695 | 696 | mov dx,[es:esi+0x22] ;登记0特权级堆栈段选择子 697 | mov [es:ecx+8],dx ;到TSS中 698 | 699 | mov edx,[es:esi+0x32] ;登记1特权级堆栈初始ESP 700 | mov [es:ecx+12],edx ;到TSS中 701 | 702 | mov dx,[es:esi+0x30] ;登记1特权级堆栈段选择子 703 | mov [es:ecx+16],dx ;到TSS中 704 | 705 | mov edx,[es:esi+0x40] ;登记2特权级堆栈初始ESP 706 | mov [es:ecx+20],edx ;到TSS中 707 | 708 | mov dx,[es:esi+0x3e] ;登记2特权级堆栈段选择子 709 | mov [es:ecx+24],dx ;到TSS中 710 | 711 | mov dx,[es:esi+0x10] ;登记任务的LDT选择子 712 | mov [es:ecx+96],dx ;到TSS中 713 | 714 | mov dx,[es:esi+0x12] ;登记任务的I/O位图偏移 715 | mov [es:ecx+102],dx ;到TSS中 716 | 717 | mov word [es:ecx+100],0 ;T=0 718 | 719 | ;在GDT中登记TSS描述符 720 | mov eax,[es:esi+0x14] ;TSS的起始线性地址 721 | movzx ebx,word [es:esi+0x12] ;段长度(界限) 722 | mov ecx,0x00408900 ;TSS描述符,特权级0 723 | call sys_routine_seg_sel:make_seg_descriptor 724 | call sys_routine_seg_sel:set_up_gdt_descriptor 725 | mov [es:esi+0x18],cx ;登记TSS选择子到TCB 726 | 727 | pop es ;恢复到调用此过程前的es段 728 | pop ds ;恢复到调用此过程前的ds段 729 | 730 | popad 731 | 732 | ret 8 ;丢弃调用本过程前压入的参数 733 | 734 | ;------------------------------------------------------------------------------- 735 | append_to_tcb_link: ;在TCB链上追加任务控制块 736 | ;输入:ECX=TCB线性基地址 737 | push eax 738 | push edx 739 | push ds 740 | push es 741 | 742 | mov eax,core_data_seg_sel ;令DS指向内核数据段 743 | mov ds,eax 744 | mov eax,mem_0_4_gb_seg_sel ;令ES指向0..4GB段 745 | mov es,eax 746 | 747 | mov dword [es: ecx+0x00],0 ;当前TCB指针域清零,以指示这是最 748 | ;后一个TCB 749 | 750 | mov eax,[tcb_chain] ;TCB表头指针 751 | or eax,eax ;链表为空? 752 | jz .notcb 753 | 754 | .searc: 755 | mov edx,eax 756 | mov eax,[es: edx+0x00] 757 | or eax,eax 758 | jnz .searc 759 | 760 | mov [es: edx+0x00],ecx 761 | jmp .retpc 762 | 763 | .notcb: 764 | mov [tcb_chain],ecx ;若为空表,直接令表头指针指向TCB 765 | 766 | .retpc: 767 | pop es 768 | pop ds 769 | pop edx 770 | pop eax 771 | 772 | ret 773 | 774 | ;------------------------------------------------------------------------------- 775 | start: 776 | mov ecx,core_data_seg_sel ;使ds指向核心数据段 777 | mov ds,ecx 778 | 779 | mov ebx,message_1 780 | call sys_routine_seg_sel:put_string 781 | 782 | ;显示处理器品牌信息 783 | mov eax,0x80000002 784 | cpuid 785 | mov [cpu_brand + 0x00],eax 786 | mov [cpu_brand + 0x04],ebx 787 | mov [cpu_brand + 0x08],ecx 788 | mov [cpu_brand + 0x0c],edx 789 | 790 | mov eax,0x80000003 791 | cpuid 792 | mov [cpu_brand + 0x10],eax 793 | mov [cpu_brand + 0x14],ebx 794 | mov [cpu_brand + 0x18],ecx 795 | mov [cpu_brand + 0x1c],edx 796 | 797 | mov eax,0x80000004 798 | cpuid 799 | mov [cpu_brand + 0x20],eax 800 | mov [cpu_brand + 0x24],ebx 801 | mov [cpu_brand + 0x28],ecx 802 | mov [cpu_brand + 0x2c],edx 803 | 804 | mov ebx,cpu_brnd0 ;显示处理器品牌信息 805 | call sys_routine_seg_sel:put_string 806 | mov ebx,cpu_brand 807 | call sys_routine_seg_sel:put_string 808 | mov ebx,cpu_brnd1 809 | call sys_routine_seg_sel:put_string 810 | 811 | ;以下开始安装为整个系统服务的调用门。特权级之间的控制转移必须使用门 812 | mov edi,salt ;C-SALT表的起始位置 813 | mov ecx,salt_items ;C-SALT表的条目数量 814 | .b3: 815 | push ecx 816 | mov eax,[edi+256] ;该条目入口点的32位偏移地址 817 | mov bx,[edi+260] ;该条目入口点的段选择子 818 | mov cx,1_11_0_1100_000_00000B ;特权级3的调用门(3以上的特权级才 819 | ;允许访问),0个参数(因为用寄存器 820 | ;传递参数,而没有用栈) 821 | call sys_routine_seg_sel:make_gate_descriptor 822 | call sys_routine_seg_sel:set_up_gdt_descriptor 823 | mov [edi+260],cx ;将返回的门描述符选择子回填 824 | add edi,salt_item_len ;指向下一个C-SALT条目 825 | pop ecx 826 | loop .b3 827 | 828 | ;对门进行测试 829 | mov ebx,message_2 830 | call far [salt_1+256] ;通过门显示信息(偏移量将被忽略) 831 | 832 | mov ebx,message_3 833 | call sys_routine_seg_sel:put_string ;在内核中调用例程不需要通过门 834 | 835 | ;创建任务控制块。这不是处理器的要求,而是我们自己为了方便而设立的 836 | mov ecx,0x46 837 | call sys_routine_seg_sel:allocate_memory 838 | call append_to_tcb_link ;将任务控制块追加到TCB链表 839 | 840 | push dword 50 ;用户程序位于逻辑50扇区 841 | push ecx ;压入任务控制块起始线性地址 842 | 843 | call load_relocate_program 844 | 845 | mov ebx,do_status 846 | call sys_routine_seg_sel:put_string 847 | 848 | mov eax,mem_0_4_gb_seg_sel 849 | mov ds,eax 850 | 851 | ltr [ecx+0x18] ;加载任务状态段 852 | lldt [ecx+0x10] ;加载LDT 853 | 854 | mov eax,[ecx+0x44] 855 | mov ds,eax ;切换到用户程序头部段 856 | 857 | ;以下假装是从调用门返回。摹仿处理器压入返回参数 858 | push dword [0x08] ;调用前的堆栈段选择子 859 | push dword 0 ;调用前的esp 860 | 861 | push dword [0x14] ;调用前的代码段选择子 862 | push dword [0x10] ;调用前的eip 863 | 864 | retf 865 | 866 | return_point: ;用户程序返回点 867 | mov eax,core_data_seg_sel ;因为c14.asm是以JMP的方式使用调 868 | mov ds,eax ;用门@TerminateProgram,回到这 869 | ;里时,特权级为3,会导致异常。 870 | mov ebx,message_6 871 | call sys_routine_seg_sel:put_string 872 | 873 | hlt 874 | 875 | core_code_end: 876 | 877 | ;------------------------------------------------------------------------------- 878 | SECTION core_trail 879 | ;------------------------------------------------------------------------------- 880 | core_end: -------------------------------------------------------------------------------- /booktool/c15/c15.asm: -------------------------------------------------------------------------------- 1 | ;代码清单15-2 2 | ;文件名:c15.asm 3 | ;文件说明:用户程序 4 | ;创建日期:2011-11-15 19:11 5 | 6 | ;=============================================================================== 7 | SECTION header vstart=0 8 | 9 | program_length dd program_end ;程序总长度#0x00 10 | 11 | head_len dd header_end ;程序头部的长度#0x04 12 | 13 | stack_seg dd 0 ;用于接收堆栈段选择子#0x08 14 | stack_len dd 1 ;程序建议的堆栈大小#0x0c 15 | ;以4KB为单位 16 | 17 | prgentry dd start ;程序入口#0x10 18 | code_seg dd section.code.start ;代码段位置#0x14 19 | code_len dd code_end ;代码段长度#0x18 20 | 21 | data_seg dd section.data.start ;数据段位置#0x1c 22 | data_len dd data_end ;数据段长度#0x20 23 | ;------------------------------------------------------------------------------- 24 | ;符号地址检索表 25 | salt_items dd (header_end-salt)/256 ;#0x24 26 | 27 | salt: ;#0x28 28 | PrintString db '@PrintString' 29 | times 256-($-PrintString) db 0 30 | 31 | TerminateProgram db '@TerminateProgram' 32 | times 256-($-TerminateProgram) db 0 33 | 34 | ReadDiskData db '@ReadDiskData' 35 | times 256-($-ReadDiskData) db 0 36 | 37 | header_end: 38 | 39 | ;=============================================================================== 40 | SECTION data vstart=0 41 | 42 | message_1 db 0x0d,0x0a 43 | db '[USER TASK]: Hi! nice to meet you,' 44 | db 'I am run at CPL=',0 45 | 46 | message_2 db 0 47 | db '.Now,I must exit...',0x0d,0x0a,0 48 | 49 | data_end: 50 | 51 | ;=============================================================================== 52 | [bits 32] 53 | ;=============================================================================== 54 | SECTION code vstart=0 55 | start: 56 | ;任务启动时,DS指向头部段,也不需要设置堆栈 57 | mov eax,ds 58 | mov fs,eax 59 | 60 | mov eax,[data_seg] 61 | mov ds,eax 62 | 63 | mov ebx,message_1 64 | call far [fs:PrintString] 65 | 66 | mov ax,cs 67 | and al,0000_0011B 68 | or al,0x0030 69 | mov [message_2],al 70 | 71 | mov ebx,message_2 72 | call far [fs:PrintString] 73 | 74 | call far [fs:TerminateProgram] ;退出,并将控制权返回到核心 75 | 76 | code_end: 77 | 78 | ;------------------------------------------------------------------------------- 79 | SECTION trail 80 | ;------------------------------------------------------------------------------- 81 | program_end: -------------------------------------------------------------------------------- /booktool/c15/c15_core.asm: -------------------------------------------------------------------------------- 1 | ;代码清单15-1 2 | ;文件名:c15_core.asm 3 | ;文件说明:保护模式微型核心程序 4 | ;创建日期:2011-11-19 21:40 5 | 6 | ;以下常量定义部分。内核的大部分内容都应当固定 7 | core_code_seg_sel equ 0x38 ;内核代码段选择子 8 | core_data_seg_sel equ 0x30 ;内核数据段选择子 9 | sys_routine_seg_sel equ 0x28 ;系统公共例程代码段的选择子 10 | video_ram_seg_sel equ 0x20 ;视频显示缓冲区的段选择子 11 | core_stack_seg_sel equ 0x18 ;内核堆栈段选择子 12 | mem_0_4_gb_seg_sel equ 0x08 ;整个0-4GB内存的段的选择子 13 | 14 | ;------------------------------------------------------------------------------- 15 | ;以下是系统核心的头部,用于加载核心程序 16 | core_length dd core_end ;核心程序总长度#00 17 | 18 | sys_routine_seg dd section.sys_routine.start 19 | ;系统公用例程段位置#04 20 | 21 | core_data_seg dd section.core_data.start 22 | ;核心数据段位置#08 23 | 24 | core_code_seg dd section.core_code.start 25 | ;核心代码段位置#0c 26 | 27 | 28 | core_entry dd start ;核心代码段入口点#10 29 | dw core_code_seg_sel 30 | 31 | ;=============================================================================== 32 | [bits 32] 33 | ;=============================================================================== 34 | SECTION sys_routine vstart=0 ;系统公共例程代码段 35 | ;------------------------------------------------------------------------------- 36 | ;字符串显示例程 37 | put_string: ;显示0终止的字符串并移动光标 38 | ;输入:DS:EBX=串地址 39 | push ecx 40 | .getc: 41 | mov cl,[ebx] 42 | or cl,cl 43 | jz .exit 44 | call put_char 45 | inc ebx 46 | jmp .getc 47 | 48 | .exit: 49 | pop ecx 50 | retf ;段间返回 51 | 52 | ;------------------------------------------------------------------------------- 53 | put_char: ;在当前光标处显示一个字符,并推进 54 | ;光标。仅用于段内调用 55 | ;输入:CL=字符ASCII码 56 | pushad 57 | 58 | ;以下取当前光标位置 59 | mov dx,0x3d4 60 | mov al,0x0e 61 | out dx,al 62 | inc dx ;0x3d5 63 | in al,dx ;高字 64 | mov ah,al 65 | 66 | dec dx ;0x3d4 67 | mov al,0x0f 68 | out dx,al 69 | inc dx ;0x3d5 70 | in al,dx ;低字 71 | mov bx,ax ;BX=代表光标位置的16位数 72 | 73 | cmp cl,0x0d ;回车符? 74 | jnz .put_0a 75 | mov ax,bx 76 | mov bl,80 77 | div bl 78 | mul bl 79 | mov bx,ax 80 | jmp .set_cursor 81 | 82 | .put_0a: 83 | cmp cl,0x0a ;换行符? 84 | jnz .put_other 85 | add bx,80 86 | jmp .roll_screen 87 | 88 | .put_other: ;正常显示字符 89 | push es 90 | mov eax,video_ram_seg_sel ;0xb8000段的选择子 91 | mov es,eax 92 | shl bx,1 93 | mov [es:bx],cl 94 | pop es 95 | 96 | ;以下将光标位置推进一个字符 97 | shr bx,1 98 | inc bx 99 | 100 | .roll_screen: 101 | cmp bx,2000 ;光标超出屏幕?滚屏 102 | jl .set_cursor 103 | 104 | push ds 105 | push es 106 | mov eax,video_ram_seg_sel 107 | mov ds,eax 108 | mov es,eax 109 | cld 110 | mov esi,0xa0 ;小心!32位模式下movsb/w/d 111 | mov edi,0x00 ;使用的是esi/edi/ecx 112 | mov ecx,1920 113 | rep movsd 114 | mov bx,3840 ;清除屏幕最底一行 115 | mov ecx,80 ;32位程序应该使用ECX 116 | .cls: 117 | mov word[es:bx],0x0720 118 | add bx,2 119 | loop .cls 120 | 121 | pop es 122 | pop ds 123 | 124 | mov bx,1920 125 | 126 | .set_cursor: 127 | mov dx,0x3d4 128 | mov al,0x0e 129 | out dx,al 130 | inc dx ;0x3d5 131 | mov al,bh 132 | out dx,al 133 | dec dx ;0x3d4 134 | mov al,0x0f 135 | out dx,al 136 | inc dx ;0x3d5 137 | mov al,bl 138 | out dx,al 139 | 140 | popad 141 | 142 | ret 143 | 144 | ;------------------------------------------------------------------------------- 145 | read_hard_disk_0: ;从硬盘读取一个逻辑扇区 146 | ;EAX=逻辑扇区号 147 | ;DS:EBX=目标缓冲区地址 148 | ;返回:EBX=EBX+512 149 | push eax 150 | push ecx 151 | push edx 152 | 153 | push eax 154 | 155 | mov dx,0x1f2 156 | mov al,1 157 | out dx,al ;读取的扇区数 158 | 159 | inc dx ;0x1f3 160 | pop eax 161 | out dx,al ;LBA地址7~0 162 | 163 | inc dx ;0x1f4 164 | mov cl,8 165 | shr eax,cl 166 | out dx,al ;LBA地址15~8 167 | 168 | inc dx ;0x1f5 169 | shr eax,cl 170 | out dx,al ;LBA地址23~16 171 | 172 | inc dx ;0x1f6 173 | shr eax,cl 174 | or al,0xe0 ;第一硬盘 LBA地址27~24 175 | out dx,al 176 | 177 | inc dx ;0x1f7 178 | mov al,0x20 ;读命令 179 | out dx,al 180 | 181 | .waits: 182 | in al,dx 183 | and al,0x88 184 | cmp al,0x08 185 | jnz .waits ;不忙,且硬盘已准备好数据传输 186 | 187 | mov ecx,256 ;总共要读取的字数 188 | mov dx,0x1f0 189 | .readw: 190 | in ax,dx 191 | mov [ebx],ax 192 | add ebx,2 193 | loop .readw 194 | 195 | pop edx 196 | pop ecx 197 | pop eax 198 | 199 | retf ;段间返回 200 | 201 | ;------------------------------------------------------------------------------- 202 | ;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助 203 | put_hex_dword: ;在当前光标处以十六进制形式显示 204 | ;一个双字并推进光标 205 | ;输入:EDX=要转换并显示的数字 206 | ;输出:无 207 | pushad 208 | push ds 209 | 210 | mov ax,core_data_seg_sel ;切换到核心数据段 211 | mov ds,ax 212 | 213 | mov ebx,bin_hex ;指向核心数据段内的转换表 214 | mov ecx,8 215 | .xlt: 216 | rol edx,4 217 | mov eax,edx 218 | and eax,0x0000000f 219 | xlat 220 | 221 | push ecx 222 | mov cl,al 223 | call put_char 224 | pop ecx 225 | 226 | loop .xlt 227 | 228 | pop ds 229 | popad 230 | retf 231 | 232 | ;------------------------------------------------------------------------------- 233 | allocate_memory: ;分配内存 234 | ;输入:ECX=希望分配的字节数 235 | ;输出:ECX=起始线性地址 236 | push ds 237 | push eax 238 | push ebx 239 | 240 | mov eax,core_data_seg_sel 241 | mov ds,eax 242 | 243 | mov eax,[ram_alloc] 244 | add eax,ecx ;下一次分配时的起始地址 245 | 246 | ;这里应当有检测可用内存数量的指令 247 | 248 | mov ecx,[ram_alloc] ;返回分配的起始地址 249 | 250 | mov ebx,eax 251 | and ebx,0xfffffffc 252 | add ebx,4 ;强制对齐 253 | test eax,0x00000003 ;下次分配的起始地址最好是4字节对齐 254 | cmovnz eax,ebx ;如果没有对齐,则强制对齐 255 | mov [ram_alloc],eax ;下次从该地址分配内存 256 | ;cmovcc指令可以避免控制转移 257 | pop ebx 258 | pop eax 259 | pop ds 260 | 261 | retf 262 | 263 | ;------------------------------------------------------------------------------- 264 | set_up_gdt_descriptor: ;在GDT内安装一个新的描述符 265 | ;输入:EDX:EAX=描述符 266 | ;输出:CX=描述符的选择子 267 | push eax 268 | push ebx 269 | push edx 270 | 271 | push ds 272 | push es 273 | 274 | mov ebx,core_data_seg_sel ;切换到核心数据段 275 | mov ds,ebx 276 | 277 | sgdt [pgdt] ;以便开始处理GDT 278 | 279 | mov ebx,mem_0_4_gb_seg_sel 280 | mov es,ebx 281 | 282 | movzx ebx,word [pgdt] ;GDT界限 283 | inc bx ;GDT总字节数,也是下一个描述符偏移 284 | add ebx,[pgdt+2] ;下一个描述符的线性地址 285 | 286 | mov [es:ebx],eax 287 | mov [es:ebx+4],edx 288 | 289 | add word [pgdt],8 ;增加一个描述符的大小 290 | 291 | lgdt [pgdt] ;对GDT的更改生效 292 | 293 | mov ax,[pgdt] ;得到GDT界限值 294 | xor dx,dx 295 | mov bx,8 296 | div bx ;除以8,去掉余数 297 | mov cx,ax 298 | shl cx,3 ;将索引号移到正确位置 299 | 300 | pop es 301 | pop ds 302 | 303 | pop edx 304 | pop ebx 305 | pop eax 306 | 307 | retf 308 | ;------------------------------------------------------------------------------- 309 | make_seg_descriptor: ;构造存储器和系统的段描述符 310 | ;输入:EAX=线性基地址 311 | ; EBX=段界限 312 | ; ECX=属性。各属性位都在原始 313 | ; 位置,无关的位清零 314 | ;返回:EDX:EAX=描述符 315 | mov edx,eax 316 | shl eax,16 317 | or ax,bx ;描述符前32位(EAX)构造完毕 318 | 319 | and edx,0xffff0000 ;清除基地址中无关的位 320 | rol edx,8 321 | bswap edx ;装配基址的31~24和23~16 (80486+) 322 | 323 | xor bx,bx 324 | or edx,ebx ;装配段界限的高4位 325 | 326 | or edx,ecx ;装配属性 327 | 328 | retf 329 | 330 | ;------------------------------------------------------------------------------- 331 | make_gate_descriptor: ;构造门的描述符(调用门等) 332 | ;输入:EAX=门代码在段内偏移地址 333 | ; BX=门代码所在段的选择子 334 | ; CX=段类型及属性等(各属 335 | ; 性位都在原始位置) 336 | ;返回:EDX:EAX=完整的描述符 337 | push ebx 338 | push ecx 339 | 340 | mov edx,eax 341 | and edx,0xffff0000 ;得到偏移地址高16位 342 | or dx,cx ;组装属性部分到EDX 343 | 344 | and eax,0x0000ffff ;得到偏移地址低16位 345 | shl ebx,16 346 | or eax,ebx ;组装段选择子部分 347 | 348 | pop ecx 349 | pop ebx 350 | 351 | retf 352 | 353 | ;------------------------------------------------------------------------------- 354 | terminate_current_task: ;终止当前任务 355 | ;注意,执行此例程时,当前任务仍在 356 | ;运行中。此例程其实也是当前任务的 357 | ;一部分 358 | pushfd 359 | mov edx,[esp] ;获得EFLAGS寄存器内容 360 | add esp,4 ;恢复堆栈指针 361 | 362 | mov eax,core_data_seg_sel 363 | mov ds,eax 364 | 365 | test dx,0100_0000_0000_0000B ;测试NT位 366 | jnz .b1 ;当前任务是嵌套的,到.b1执行iretd 367 | mov ebx,core_msg1 ;当前任务不是嵌套的,直接切换到 368 | call sys_routine_seg_sel:put_string 369 | jmp far [prgman_tss] ;程序管理器任务 370 | 371 | .b1: 372 | mov ebx,core_msg0 373 | call sys_routine_seg_sel:put_string 374 | iretd 375 | 376 | sys_routine_end: 377 | 378 | ;=============================================================================== 379 | SECTION core_data vstart=0 ;系统核心的数据段 380 | ;------------------------------------------------------------------------------- 381 | pgdt dw 0 ;用于设置和修改GDT 382 | dd 0 383 | 384 | ram_alloc dd 0x00100000 ;下次分配内存时的起始地址 385 | 386 | ;符号地址检索表 387 | salt: 388 | salt_1 db '@PrintString' 389 | times 256-($-salt_1) db 0 390 | dd put_string 391 | dw sys_routine_seg_sel 392 | 393 | salt_2 db '@ReadDiskData' 394 | times 256-($-salt_2) db 0 395 | dd read_hard_disk_0 396 | dw sys_routine_seg_sel 397 | 398 | salt_3 db '@PrintDwordAsHexString' 399 | times 256-($-salt_3) db 0 400 | dd put_hex_dword 401 | dw sys_routine_seg_sel 402 | 403 | salt_4 db '@TerminateProgram' 404 | times 256-($-salt_4) db 0 405 | dd terminate_current_task 406 | dw sys_routine_seg_sel 407 | 408 | salt_item_len equ $-salt_4 409 | salt_items equ ($-salt)/salt_item_len 410 | 411 | message_1 db ' If you seen this message,that means we ' 412 | db 'are now in protect mode,and the system ' 413 | db 'core is loaded,and the video display ' 414 | db 'routine works perfectly.',0x0d,0x0a,0 415 | 416 | message_2 db ' System wide CALL-GATE mounted.',0x0d,0x0a,0 417 | 418 | bin_hex db '0123456789ABCDEF' 419 | ;put_hex_dword子过程用的查找表 420 | 421 | core_buf times 2048 db 0 ;内核用的缓冲区 422 | 423 | cpu_brnd0 db 0x0d,0x0a,' ',0 424 | cpu_brand times 52 db 0 425 | cpu_brnd1 db 0x0d,0x0a,0x0d,0x0a,0 426 | 427 | ;任务控制块链 428 | tcb_chain dd 0 429 | 430 | ;程序管理器的任务信息 431 | prgman_tss dd 0 ;程序管理器的TSS基地址 432 | dw 0 ;程序管理器的TSS描述符选择子 433 | 434 | prgman_msg1 db 0x0d,0x0a 435 | db '[PROGRAM MANAGER]: Hello! I am Program Manager,' 436 | db 'run at CPL=0.Now,create user task and switch ' 437 | db 'to it by the CALL instruction...',0x0d,0x0a,0 438 | 439 | prgman_msg2 db 0x0d,0x0a 440 | db '[PROGRAM MANAGER]: I am glad to regain control.' 441 | db 'Now,create another user task and switch to ' 442 | db 'it by the JMP instruction...',0x0d,0x0a,0 443 | 444 | prgman_msg3 db 0x0d,0x0a 445 | db '[PROGRAM MANAGER]: I am gain control again,' 446 | db 'HALT...',0 447 | 448 | core_msg0 db 0x0d,0x0a 449 | db '[SYSTEM CORE]: Uh...This task initiated with ' 450 | db 'CALL instruction or an exeception/ interrupt,' 451 | db 'should use IRETD instruction to switch back...' 452 | db 0x0d,0x0a,0 453 | 454 | core_msg1 db 0x0d,0x0a 455 | db '[SYSTEM CORE]: Uh...This task initiated with ' 456 | db 'JMP instruction, should switch to Program ' 457 | db 'Manager directly by the JMP instruction...' 458 | db 0x0d,0x0a,0 459 | 460 | core_data_end: 461 | 462 | ;=============================================================================== 463 | SECTION core_code vstart=0 464 | ;------------------------------------------------------------------------------- 465 | fill_descriptor_in_ldt: ;在LDT内安装一个新的描述符 466 | ;输入:EDX:EAX=描述符 467 | ; EBX=TCB基地址 468 | ;输出:CX=描述符的选择子 469 | push eax 470 | push edx 471 | push edi 472 | push ds 473 | 474 | mov ecx,mem_0_4_gb_seg_sel 475 | mov ds,ecx 476 | 477 | mov edi,[ebx+0x0c] ;获得LDT基地址 478 | 479 | xor ecx,ecx 480 | mov cx,[ebx+0x0a] ;获得LDT界限 481 | inc cx ;LDT的总字节数,即新描述符偏移地址 482 | 483 | mov [edi+ecx+0x00],eax 484 | mov [edi+ecx+0x04],edx ;安装描述符 485 | 486 | add cx,8 487 | dec cx ;得到新的LDT界限值 488 | 489 | mov [ebx+0x0a],cx ;更新LDT界限值到TCB 490 | 491 | mov ax,cx 492 | xor dx,dx 493 | mov cx,8 494 | div cx 495 | 496 | mov cx,ax 497 | shl cx,3 ;左移3位,并且 498 | or cx,0000_0000_0000_0100B ;使TI位=1,指向LDT,最后使RPL=00 499 | 500 | pop ds 501 | pop edi 502 | pop edx 503 | pop eax 504 | 505 | ret 506 | 507 | ;------------------------------------------------------------------------------- 508 | load_relocate_program: ;加载并重定位用户程序 509 | ;输入: PUSH 逻辑扇区号 510 | ; PUSH 任务控制块基地址 511 | ;输出:无 512 | pushad 513 | 514 | push ds 515 | push es 516 | 517 | mov ebp,esp ;为访问通过堆栈传递的参数做准备 518 | 519 | mov ecx,mem_0_4_gb_seg_sel 520 | mov es,ecx 521 | 522 | mov esi,[ebp+11*4] ;从堆栈中取得TCB的基地址 523 | 524 | ;以下申请创建LDT所需要的内存 525 | mov ecx,160 ;允许安装20个LDT描述符 526 | call sys_routine_seg_sel:allocate_memory 527 | mov [es:esi+0x0c],ecx ;登记LDT基地址到TCB中 528 | mov word [es:esi+0x0a],0xffff ;登记LDT初始的界限到TCB中 529 | 530 | ;以下开始加载用户程序 531 | mov eax,core_data_seg_sel 532 | mov ds,eax ;切换DS到内核数据段 533 | 534 | mov eax,[ebp+12*4] ;从堆栈中取出用户程序起始扇区号 535 | mov ebx,core_buf ;读取程序头部数据 536 | call sys_routine_seg_sel:read_hard_disk_0 537 | 538 | ;以下判断整个程序有多大 539 | mov eax,[core_buf] ;程序尺寸 540 | mov ebx,eax 541 | and ebx,0xfffffe00 ;使之512字节对齐(能被512整除的数低 542 | add ebx,512 ;9位都为0 543 | test eax,0x000001ff ;程序的大小正好是512的倍数吗? 544 | cmovnz eax,ebx ;不是。使用凑整的结果 545 | 546 | mov ecx,eax ;实际需要申请的内存数量 547 | call sys_routine_seg_sel:allocate_memory 548 | mov [es:esi+0x06],ecx ;登记程序加载基地址到TCB中 549 | 550 | mov ebx,ecx ;ebx -> 申请到的内存首地址 551 | xor edx,edx 552 | mov ecx,512 553 | div ecx 554 | mov ecx,eax ;总扇区数 555 | 556 | mov eax,mem_0_4_gb_seg_sel ;切换DS到0-4GB的段 557 | mov ds,eax 558 | 559 | mov eax,[ebp+12*4] ;起始扇区号 560 | .b1: 561 | call sys_routine_seg_sel:read_hard_disk_0 562 | inc eax 563 | loop .b1 ;循环读,直到读完整个用户程序 564 | 565 | mov edi,[es:esi+0x06] ;获得程序加载基地址 566 | 567 | ;建立程序头部段描述符 568 | mov eax,edi ;程序头部起始线性地址 569 | mov ebx,[edi+0x04] ;段长度 570 | dec ebx ;段界限 571 | mov ecx,0x0040f200 ;字节粒度的数据段描述符,特权级3 572 | call sys_routine_seg_sel:make_seg_descriptor 573 | 574 | ;安装头部段描述符到LDT中 575 | mov ebx,esi ;TCB的基地址 576 | call fill_descriptor_in_ldt 577 | 578 | or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 579 | mov [es:esi+0x44],cx ;登记程序头部段选择子到TCB 580 | mov [edi+0x04],cx ;和头部内 581 | 582 | ;建立程序代码段描述符 583 | mov eax,edi 584 | add eax,[edi+0x14] ;代码起始线性地址 585 | mov ebx,[edi+0x18] ;段长度 586 | dec ebx ;段界限 587 | mov ecx,0x0040f800 ;字节粒度的代码段描述符,特权级3 588 | call sys_routine_seg_sel:make_seg_descriptor 589 | mov ebx,esi ;TCB的基地址 590 | call fill_descriptor_in_ldt 591 | or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 592 | mov [edi+0x14],cx ;登记代码段选择子到头部 593 | 594 | ;建立程序数据段描述符 595 | mov eax,edi 596 | add eax,[edi+0x1c] ;数据段起始线性地址 597 | mov ebx,[edi+0x20] ;段长度 598 | dec ebx ;段界限 599 | mov ecx,0x0040f200 ;字节粒度的数据段描述符,特权级3 600 | call sys_routine_seg_sel:make_seg_descriptor 601 | mov ebx,esi ;TCB的基地址 602 | call fill_descriptor_in_ldt 603 | or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 604 | mov [edi+0x1c],cx ;登记数据段选择子到头部 605 | 606 | ;建立程序堆栈段描述符 607 | mov ecx,[edi+0x0c] ;4KB的倍率 608 | mov ebx,0x000fffff 609 | sub ebx,ecx ;得到段界限 610 | mov eax,4096 611 | mul ecx 612 | mov ecx,eax ;准备为堆栈分配内存 613 | call sys_routine_seg_sel:allocate_memory 614 | add eax,ecx ;得到堆栈的高端物理地址 615 | mov ecx,0x00c0f600 ;字节粒度的堆栈段描述符,特权级3 616 | call sys_routine_seg_sel:make_seg_descriptor 617 | mov ebx,esi ;TCB的基地址 618 | call fill_descriptor_in_ldt 619 | or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 620 | mov [edi+0x08],cx ;登记堆栈段选择子到头部 621 | 622 | ;重定位SALT 623 | mov eax,mem_0_4_gb_seg_sel ;这里和前一章不同,头部段描述符 624 | mov es,eax ;已安装,但还没有生效,故只能通 625 | ;过4GB段访问用户程序头部 626 | mov eax,core_data_seg_sel 627 | mov ds,eax 628 | 629 | cld 630 | 631 | mov ecx,[es:edi+0x24] ;U-SALT条目数(通过访问4GB段取得) 632 | add edi,0x28 ;U-SALT在4GB段内的偏移 633 | .b2: 634 | push ecx 635 | push edi 636 | 637 | mov ecx,salt_items 638 | mov esi,salt 639 | .b3: 640 | push edi 641 | push esi 642 | push ecx 643 | 644 | mov ecx,64 ;检索表中,每条目的比较次数 645 | repe cmpsd ;每次比较4字节 646 | jnz .b4 647 | mov eax,[esi] ;若匹配,则esi恰好指向其后的地址 648 | mov [es:edi-256],eax ;将字符串改写成偏移地址 649 | mov ax,[esi+4] 650 | or ax,0000000000000011B ;以用户程序自己的特权级使用调用门 651 | ;故RPL=3 652 | mov [es:edi-252],ax ;回填调用门选择子 653 | .b4: 654 | 655 | pop ecx 656 | pop esi 657 | add esi,salt_item_len 658 | pop edi ;从头比较 659 | loop .b3 660 | 661 | pop edi 662 | add edi,256 663 | pop ecx 664 | loop .b2 665 | 666 | mov esi,[ebp+11*4] ;从堆栈中取得TCB的基地址 667 | 668 | ;创建0特权级堆栈 669 | mov ecx,4096 670 | mov eax,ecx ;为生成堆栈高端地址做准备 671 | mov [es:esi+0x1a],ecx 672 | shr dword [es:esi+0x1a],12 ;登记0特权级堆栈尺寸到TCB 673 | call sys_routine_seg_sel:allocate_memory 674 | add eax,ecx ;堆栈必须使用高端地址为基地址 675 | mov [es:esi+0x1e],eax ;登记0特权级堆栈基地址到TCB 676 | mov ebx,0xffffe ;段长度(界限) 677 | mov ecx,0x00c09600 ;4KB粒度,读写,特权级0 678 | call sys_routine_seg_sel:make_seg_descriptor 679 | mov ebx,esi ;TCB的基地址 680 | call fill_descriptor_in_ldt 681 | ;or cx,0000_0000_0000_0000 ;设置选择子的特权级为0 682 | mov [es:esi+0x22],cx ;登记0特权级堆栈选择子到TCB 683 | mov dword [es:esi+0x24],0 ;登记0特权级堆栈初始ESP到TCB 684 | 685 | ;创建1特权级堆栈 686 | mov ecx,4096 687 | mov eax,ecx ;为生成堆栈高端地址做准备 688 | mov [es:esi+0x28],ecx 689 | shr [es:esi+0x28],12 ;登记1特权级堆栈尺寸到TCB 690 | call sys_routine_seg_sel:allocate_memory 691 | add eax,ecx ;堆栈必须使用高端地址为基地址 692 | mov [es:esi+0x2c],eax ;登记1特权级堆栈基地址到TCB 693 | mov ebx,0xffffe ;段长度(界限) 694 | mov ecx,0x00c0b600 ;4KB粒度,读写,特权级1 695 | call sys_routine_seg_sel:make_seg_descriptor 696 | mov ebx,esi ;TCB的基地址 697 | call fill_descriptor_in_ldt 698 | or cx,0000_0000_0000_0001 ;设置选择子的特权级为1 699 | mov [es:esi+0x30],cx ;登记1特权级堆栈选择子到TCB 700 | mov dword [es:esi+0x32],0 ;登记1特权级堆栈初始ESP到TCB 701 | 702 | ;创建2特权级堆栈 703 | mov ecx,4096 704 | mov eax,ecx ;为生成堆栈高端地址做准备 705 | mov [es:esi+0x36],ecx 706 | shr [es:esi+0x36],12 ;登记2特权级堆栈尺寸到TCB 707 | call sys_routine_seg_sel:allocate_memory 708 | add eax,ecx ;堆栈必须使用高端地址为基地址 709 | mov [es:esi+0x3a],ecx ;登记2特权级堆栈基地址到TCB 710 | mov ebx,0xffffe ;段长度(界限) 711 | mov ecx,0x00c0d600 ;4KB粒度,读写,特权级2 712 | call sys_routine_seg_sel:make_seg_descriptor 713 | mov ebx,esi ;TCB的基地址 714 | call fill_descriptor_in_ldt 715 | or cx,0000_0000_0000_0010 ;设置选择子的特权级为2 716 | mov [es:esi+0x3e],cx ;登记2特权级堆栈选择子到TCB 717 | mov dword [es:esi+0x40],0 ;登记2特权级堆栈初始ESP到TCB 718 | 719 | ;在GDT中登记LDT描述符 720 | mov eax,[es:esi+0x0c] ;LDT的起始线性地址 721 | movzx ebx,word [es:esi+0x0a] ;LDT段界限 722 | mov ecx,0x00408200 ;LDT描述符,特权级0 723 | call sys_routine_seg_sel:make_seg_descriptor 724 | call sys_routine_seg_sel:set_up_gdt_descriptor 725 | mov [es:esi+0x10],cx ;登记LDT选择子到TCB中 726 | 727 | ;创建用户程序的TSS 728 | mov ecx,104 ;tss的基本尺寸 729 | mov [es:esi+0x12],cx 730 | dec word [es:esi+0x12] ;登记TSS界限值到TCB 731 | call sys_routine_seg_sel:allocate_memory 732 | mov [es:esi+0x14],ecx ;登记TSS基地址到TCB 733 | 734 | ;登记基本的TSS表格内容 735 | mov word [es:ecx+0],0 ;反向链=0 736 | 737 | mov edx,[es:esi+0x24] ;登记0特权级堆栈初始ESP 738 | mov [es:ecx+4],edx ;到TSS中 739 | 740 | mov dx,[es:esi+0x22] ;登记0特权级堆栈段选择子 741 | mov [es:ecx+8],dx ;到TSS中 742 | 743 | mov edx,[es:esi+0x32] ;登记1特权级堆栈初始ESP 744 | mov [es:ecx+12],edx ;到TSS中 745 | 746 | mov dx,[es:esi+0x30] ;登记1特权级堆栈段选择子 747 | mov [es:ecx+16],dx ;到TSS中 748 | 749 | mov edx,[es:esi+0x40] ;登记2特权级堆栈初始ESP 750 | mov [es:ecx+20],edx ;到TSS中 751 | 752 | mov dx,[es:esi+0x3e] ;登记2特权级堆栈段选择子 753 | mov [es:ecx+24],dx ;到TSS中 754 | 755 | mov dx,[es:esi+0x10] ;登记任务的LDT选择子 756 | mov [es:ecx+96],dx ;到TSS中 757 | 758 | mov dx,[es:esi+0x12] ;登记任务的I/O位图偏移 759 | mov [es:ecx+102],dx ;到TSS中 760 | 761 | mov word [es:ecx+100],0 ;T=0 762 | 763 | mov dword [es:ecx+28],0 ;登记CR3(PDBR) 764 | 765 | ;访问用户程序头部,获取数据填充TSS 766 | mov ebx,[ebp+11*4] ;从堆栈中取得TCB的基地址 767 | mov edi,[es:ebx+0x06] ;用户程序加载的基地址 768 | 769 | mov edx,[es:edi+0x10] ;登记程序入口点(EIP) 770 | mov [es:ecx+32],edx ;到TSS 771 | 772 | mov dx,[es:edi+0x14] ;登记程序代码段(CS)选择子 773 | mov [es:ecx+76],dx ;到TSS中 774 | 775 | mov dx,[es:edi+0x08] ;登记程序堆栈段(SS)选择子 776 | mov [es:ecx+80],dx ;到TSS中 777 | 778 | mov dx,[es:edi+0x04] ;登记程序数据段(DS)选择子 779 | mov word [es:ecx+84],dx ;到TSS中。注意,它指向程序头部段 780 | 781 | mov word [es:ecx+72],0 ;TSS中的ES=0 782 | 783 | mov word [es:ecx+88],0 ;TSS中的FS=0 784 | 785 | mov word [es:ecx+92],0 ;TSS中的GS=0 786 | 787 | pushfd 788 | pop edx 789 | 790 | mov dword [es:ecx+36],edx ;EFLAGS 791 | 792 | ;在GDT中登记TSS描述符 793 | mov eax,[es:esi+0x14] ;TSS的起始线性地址 794 | movzx ebx,word [es:esi+0x12] ;段长度(界限) 795 | mov ecx,0x00408900 ;TSS描述符,特权级0 796 | call sys_routine_seg_sel:make_seg_descriptor 797 | call sys_routine_seg_sel:set_up_gdt_descriptor 798 | mov [es:esi+0x18],cx ;登记TSS选择子到TCB 799 | 800 | pop es ;恢复到调用此过程前的es段 801 | pop ds ;恢复到调用此过程前的ds段 802 | 803 | popad 804 | 805 | ret 8 ;丢弃调用本过程前压入的参数 806 | 807 | ;------------------------------------------------------------------------------- 808 | append_to_tcb_link: ;在TCB链上追加任务控制块 809 | ;输入:ECX=TCB线性基地址 810 | push eax 811 | push edx 812 | push ds 813 | push es 814 | 815 | mov eax,core_data_seg_sel ;令DS指向内核数据段 816 | mov ds,eax 817 | mov eax,mem_0_4_gb_seg_sel ;令ES指向0..4GB段 818 | mov es,eax 819 | 820 | mov dword [es: ecx+0x00],0 ;当前TCB指针域清零,以指示这是最 821 | ;后一个TCB 822 | 823 | mov eax,[tcb_chain] ;TCB表头指针 824 | or eax,eax ;链表为空? 825 | jz .notcb 826 | 827 | .searc: 828 | mov edx,eax 829 | mov eax,[es: edx+0x00] 830 | or eax,eax 831 | jnz .searc 832 | 833 | mov [es: edx+0x00],ecx 834 | jmp .retpc 835 | 836 | .notcb: 837 | mov [tcb_chain],ecx ;若为空表,直接令表头指针指向TCB 838 | 839 | .retpc: 840 | pop es 841 | pop ds 842 | pop edx 843 | pop eax 844 | 845 | ret 846 | 847 | ;------------------------------------------------------------------------------- 848 | start: 849 | mov ecx,core_data_seg_sel ;令DS指向核心数据段 850 | mov ds,ecx 851 | 852 | mov ecx,mem_0_4_gb_seg_sel ;令ES指向4GB数据段 853 | mov es,ecx 854 | 855 | mov ebx,message_1 856 | call sys_routine_seg_sel:put_string 857 | 858 | ;显示处理器品牌信息 859 | mov eax,0x80000002 860 | cpuid 861 | mov [cpu_brand + 0x00],eax 862 | mov [cpu_brand + 0x04],ebx 863 | mov [cpu_brand + 0x08],ecx 864 | mov [cpu_brand + 0x0c],edx 865 | 866 | mov eax,0x80000003 867 | cpuid 868 | mov [cpu_brand + 0x10],eax 869 | mov [cpu_brand + 0x14],ebx 870 | mov [cpu_brand + 0x18],ecx 871 | mov [cpu_brand + 0x1c],edx 872 | 873 | mov eax,0x80000004 874 | cpuid 875 | mov [cpu_brand + 0x20],eax 876 | mov [cpu_brand + 0x24],ebx 877 | mov [cpu_brand + 0x28],ecx 878 | mov [cpu_brand + 0x2c],edx 879 | 880 | mov ebx,cpu_brnd0 ;显示处理器品牌信息 881 | call sys_routine_seg_sel:put_string 882 | mov ebx,cpu_brand 883 | call sys_routine_seg_sel:put_string 884 | mov ebx,cpu_brnd1 885 | call sys_routine_seg_sel:put_string 886 | 887 | ;以下开始安装为整个系统服务的调用门。特权级之间的控制转移必须使用门 888 | mov edi,salt ;C-SALT表的起始位置 889 | mov ecx,salt_items ;C-SALT表的条目数量 890 | .b3: 891 | push ecx 892 | mov eax,[edi+256] ;该条目入口点的32位偏移地址 893 | mov bx,[edi+260] ;该条目入口点的段选择子 894 | mov cx,1_11_0_1100_000_00000B ;特权级3的调用门(3以上的特权级才 895 | ;允许访问),0个参数(因为用寄存器 896 | ;传递参数,而没有用栈) 897 | call sys_routine_seg_sel:make_gate_descriptor 898 | call sys_routine_seg_sel:set_up_gdt_descriptor 899 | mov [edi+260],cx ;将返回的门描述符选择子回填 900 | add edi,salt_item_len ;指向下一个C-SALT条目 901 | pop ecx 902 | loop .b3 903 | 904 | ;对门进行测试 905 | mov ebx,message_2 906 | call far [salt_1+256] ;通过门显示信息(偏移量将被忽略) 907 | 908 | ;为程序管理器的TSS分配内存空间 909 | mov ecx,104 ;为该任务的TSS分配内存 910 | call sys_routine_seg_sel:allocate_memory 911 | mov [prgman_tss+0x00],ecx ;保存程序管理器的TSS基地址 912 | 913 | ;在程序管理器的TSS中设置必要的项目 914 | mov word [es:ecx+96],0 ;没有LDT。处理器允许没有LDT的任务。 915 | mov word [es:ecx+102],103 ;没有I/O位图。0特权级事实上不需要。 916 | mov word [es:ecx+0],0 ;反向链=0 917 | mov dword [es:ecx+28],0 ;登记CR3(PDBR) 918 | mov word [es:ecx+100],0 ;T=0 919 | ;不需要0、1、2特权级堆栈。0特级不 920 | ;会向低特权级转移控制。 921 | 922 | ;创建TSS描述符,并安装到GDT中 923 | mov eax,ecx ;TSS的起始线性地址 924 | mov ebx,103 ;段长度(界限) 925 | mov ecx,0x00408900 ;TSS描述符,特权级0 926 | call sys_routine_seg_sel:make_seg_descriptor 927 | call sys_routine_seg_sel:set_up_gdt_descriptor 928 | mov [prgman_tss+0x04],cx ;保存程序管理器的TSS描述符选择子 929 | 930 | ;任务寄存器TR中的内容是任务存在的标志,该内容也决定了当前任务是谁。 931 | ;下面的指令为当前正在执行的0特权级任务“程序管理器”后补手续(TSS)。 932 | ltr cx 933 | 934 | ;现在可认为“程序管理器”任务正执行中 935 | mov ebx,prgman_msg1 936 | call sys_routine_seg_sel:put_string 937 | 938 | mov ecx,0x46 939 | call sys_routine_seg_sel:allocate_memory 940 | call append_to_tcb_link ;将此TCB添加到TCB链中 941 | 942 | push dword 50 ;用户程序位于逻辑50扇区 943 | push ecx ;压入任务控制块起始线性地址 944 | 945 | call load_relocate_program 946 | 947 | call far [es:ecx+0x14] ;执行任务切换。和上一章不同,任务切 948 | ;换时要恢复TSS内容,所以在创建任务 949 | ;时TSS要填写完整 950 | 951 | ;重新加载并切换任务 952 | mov ebx,prgman_msg2 953 | call sys_routine_seg_sel:put_string 954 | 955 | mov ecx,0x46 956 | call sys_routine_seg_sel:allocate_memory 957 | call append_to_tcb_link ;将此TCB添加到TCB链中 958 | 959 | push dword 50 ;用户程序位于逻辑50扇区 960 | push ecx ;压入任务控制块起始线性地址 961 | 962 | call load_relocate_program 963 | 964 | jmp far [es:ecx+0x14] ;执行任务切换 965 | 966 | mov ebx,prgman_msg3 967 | call sys_routine_seg_sel:put_string 968 | 969 | hlt 970 | 971 | core_code_end: 972 | 973 | ;------------------------------------------------------------------------------- 974 | SECTION core_trail 975 | ;------------------------------------------------------------------------------- 976 | core_end: -------------------------------------------------------------------------------- /booktool/c16/c16.asm: -------------------------------------------------------------------------------- 1 | ;代码清单16-2 2 | ;文件名:c16.asm 3 | ;文件说明:用户程序 4 | ;创建日期:2012-05-25 13:53 5 | 6 | program_length dd program_end ;程序总长度#0x00 7 | entry_point dd start ;程序入口点#0x04 8 | salt_position dd salt_begin ;SALT表起始偏移量#0x08 9 | salt_items dd (salt_end-salt_begin)/256 ;SALT条目数#0x0C 10 | 11 | ;------------------------------------------------------------------------------- 12 | 13 | ;符号地址检索表 14 | salt_begin: 15 | 16 | PrintString db '@PrintString' 17 | times 256-($-PrintString) db 0 18 | 19 | TerminateProgram db '@TerminateProgram' 20 | times 256-($-TerminateProgram) db 0 21 | ;------------------------------------------------------------------------------- 22 | 23 | reserved times 256*500 db 0 ;保留一个空白区,以演示分页 24 | 25 | ;------------------------------------------------------------------------------- 26 | ReadDiskData db '@ReadDiskData' 27 | times 256-($-ReadDiskData) db 0 28 | 29 | PrintDwordAsHex db '@PrintDwordAsHexString' 30 | times 256-($-PrintDwordAsHex) db 0 31 | 32 | salt_end: 33 | 34 | message_0 db 0x0d,0x0a, 35 | db ' ............User task is running with ' 36 | db 'paging enabled!............',0x0d,0x0a,0 37 | 38 | space db 0x20,0x20,0 39 | 40 | ;------------------------------------------------------------------------------- 41 | [bits 32] 42 | ;------------------------------------------------------------------------------- 43 | 44 | start: 45 | 46 | mov ebx,message_0 47 | call far [PrintString] 48 | 49 | xor esi,esi 50 | mov ecx,88 51 | .b1: 52 | mov ebx,space 53 | call far [PrintString] 54 | 55 | mov edx,[esi*4] 56 | call far [PrintDwordAsHex] 57 | 58 | inc esi 59 | loop .b1 60 | 61 | call far [TerminateProgram] ;退出,并将控制权返回到核心 62 | 63 | ;------------------------------------------------------------------------------- 64 | program_end: -------------------------------------------------------------------------------- /booktool/c17/c17-1.asm: -------------------------------------------------------------------------------- 1 | ;代码清单17-3 2 | ;文件名:c17_1.asm 3 | ;文件说明:用户程序 4 | ;创建日期:2012-07-14 15:46 5 | 6 | program_length dd program_end ;程序总长度#0x00 7 | entry_point dd start ;程序入口点#0x04 8 | salt_position dd salt_begin ;SALT表起始偏移量#0x08 9 | salt_items dd (salt_end-salt_begin)/256 ;SALT条目数#0x0C 10 | 11 | ;------------------------------------------------------------------------------- 12 | 13 | ;符号地址检索表 14 | salt_begin: 15 | 16 | PrintString db '@PrintString' 17 | times 256-($-PrintString) db 0 18 | 19 | TerminateProgram db '@TerminateProgram' 20 | times 256-($-TerminateProgram) db 0 21 | 22 | ReadDiskData db '@ReadDiskData' 23 | times 256-($-ReadDiskData) db 0 24 | 25 | PrintDwordAsHex db '@PrintDwordAsHexString' 26 | times 256-($-PrintDwordAsHex) db 0 27 | 28 | salt_end: 29 | 30 | message_0 db ' User task A->;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;' 31 | db 0x0d,0x0a,0 32 | 33 | ;------------------------------------------------------------------------------- 34 | [bits 32] 35 | ;------------------------------------------------------------------------------- 36 | 37 | start: 38 | 39 | mov ebx,message_0 40 | call far [PrintString] 41 | jmp start 42 | 43 | call far [TerminateProgram] ;退出,并将控制权返回到核心 44 | 45 | ;------------------------------------------------------------------------------- 46 | program_end: -------------------------------------------------------------------------------- /booktool/c17/c17-2.asm: -------------------------------------------------------------------------------- 1 | ;代码清单17-4 2 | ;文件名:c17_2.asm 3 | ;文件说明:用户程序 4 | ;创建日期:2012-07-16 12:27 5 | 6 | program_length dd program_end ;程序总长度#0x00 7 | entry_point dd start ;程序入口点#0x04 8 | salt_position dd salt_begin ;SALT表起始偏移量#0x08 9 | salt_items dd (salt_end-salt_begin)/256 ;SALT条目数#0x0C 10 | 11 | ;------------------------------------------------------------------------------- 12 | 13 | ;符号地址检索表 14 | salt_begin: 15 | 16 | PrintString db '@PrintString' 17 | times 256-($-PrintString) db 0 18 | 19 | TerminateProgram db '@TerminateProgram' 20 | times 256-($-TerminateProgram) db 0 21 | 22 | ReadDiskData db '@ReadDiskData' 23 | times 256-($-ReadDiskData) db 0 24 | 25 | PrintDwordAsHex db '@PrintDwordAsHexString' 26 | times 256-($-PrintDwordAsHex) db 0 27 | 28 | salt_end: 29 | 30 | message_0 db ' User task B->$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$' 31 | db 0x0d,0x0a,0 32 | 33 | ;------------------------------------------------------------------------------- 34 | [bits 32] 35 | ;------------------------------------------------------------------------------- 36 | 37 | start: 38 | 39 | mov ebx,message_0 40 | call far [PrintString] 41 | jmp start 42 | 43 | call far [TerminateProgram] ;退出,并将控制权返回到核心 44 | 45 | ;------------------------------------------------------------------------------- 46 | program_end: -------------------------------------------------------------------------------- /booktool/c17/c17_mbr.asm: -------------------------------------------------------------------------------- 1 | ;代码清单17-1 2 | ;文件名:c17_mbr.asm 3 | ;文件说明:硬盘主引导扇区代码 4 | ;创建日期:2012-07-13 11:20 ;设置堆栈段和栈指针 5 | 6 | core_base_address equ 0x00040000 ;常数,内核加载的起始内存地址 7 | core_start_sector equ 0x00000001 ;常数,内核的起始逻辑扇区号 8 | 9 | ;=============================================================================== 10 | SECTION mbr vstart=0x00007c00 11 | 12 | mov ax,cs 13 | mov ss,ax 14 | mov sp,0x7c00 15 | 16 | ;计算GDT所在的逻辑段地址 17 | mov eax,[cs:pgdt+0x02] ;GDT的32位物理地址 18 | xor edx,edx 19 | mov ebx,16 20 | div ebx ;分解成16位逻辑地址 21 | 22 | mov ds,eax ;令DS指向该段以进行操作 23 | mov ebx,edx ;段内起始偏移地址 24 | 25 | ;跳过0#号描述符的槽位 26 | ;创建1#描述符,保护模式下的代码段描述符 27 | mov dword [ebx+0x08],0x0000ffff ;基地址为0,界限0xFFFFF,DPL=00 28 | mov dword [ebx+0x0c],0x00cf9800 ;4KB粒度,代码段描述符,向上扩展 29 | 30 | ;创建2#描述符,保护模式下的数据段和堆栈段描述符 31 | mov dword [ebx+0x10],0x0000ffff ;基地址为0,界限0xFFFFF,DPL=00 32 | mov dword [ebx+0x14],0x00cf9200 ;4KB粒度,数据段描述符,向上扩展 33 | 34 | ;初始化描述符表寄存器GDTR 35 | mov word [cs: pgdt],23 ;描述符表的界限 36 | 37 | lgdt [cs: pgdt] 38 | 39 | in al,0x92 ;南桥芯片内的端口 40 | or al,0000_0010B 41 | out 0x92,al ;打开A20 42 | 43 | cli ;中断机制尚未工作 44 | 45 | mov eax,cr0 46 | or eax,1 47 | mov cr0,eax ;设置PE位 48 | 49 | ;以下进入保护模式... ... 50 | jmp dword 0x0008:flush ;16位的描述符选择子:32位偏移 51 | ;清流水线并串行化处理器 52 | [bits 32] 53 | flush: 54 | mov eax,0x00010 ;加载数据段(4GB)选择子 55 | mov ds,eax 56 | mov es,eax 57 | mov fs,eax 58 | mov gs,eax 59 | mov ss,eax ;加载堆栈段(4GB)选择子 60 | mov esp,0x7000 ;堆栈指针 61 | 62 | ;以下加载系统核心程序 63 | mov edi,core_base_address 64 | 65 | mov eax,core_start_sector 66 | mov ebx,edi ;起始地址 67 | call read_hard_disk_0 ;以下读取程序的起始部分(一个扇区) 68 | 69 | ;以下判断整个程序有多大 70 | mov eax,[edi] ;核心程序尺寸 71 | xor edx,edx 72 | mov ecx,512 ;512字节每扇区 73 | div ecx 74 | 75 | or edx,edx 76 | jnz @1 ;未除尽,因此结果比实际扇区数少1 77 | dec eax ;已经读了一个扇区,扇区总数减1 78 | @1: 79 | or eax,eax ;考虑实际长度≤512个字节的情况 80 | jz pge ;EAX=0 ? 81 | 82 | ;读取剩余的扇区 83 | mov ecx,eax ;32位模式下的LOOP使用ECX 84 | mov eax,core_start_sector 85 | inc eax ;从下一个逻辑扇区接着读 86 | @2: 87 | call read_hard_disk_0 88 | inc eax 89 | loop @2 ;循环读,直到读完整个内核 90 | 91 | pge: 92 | ;准备打开分页机制。从此,再也不用在段之间转来转去,实在晕乎~ 93 | 94 | ;创建系统内核的页目录表PDT 95 | mov ebx,0x00020000 ;页目录表PDT的物理地址 96 | 97 | ;在页目录内创建指向页目录表自己的目录项 98 | mov dword [ebx+4092],0x00020003 99 | 100 | mov edx,0x00021003 ;MBR空间有限,后面尽量不使用立即数 101 | ;在页目录内创建与线性地址0x00000000对应的目录项 102 | mov [ebx+0x000],edx ;写入目录项(页表的物理地址和属性) 103 | ;此目录项仅用于过渡。 104 | ;在页目录内创建与线性地址0x80000000对应的目录项 105 | mov [ebx+0x800],edx ;写入目录项(页表的物理地址和属性) 106 | 107 | ;创建与上面那个目录项相对应的页表,初始化页表项 108 | mov ebx,0x00021000 ;页表的物理地址 109 | xor eax,eax ;起始页的物理地址 110 | xor esi,esi 111 | .b1: 112 | mov edx,eax 113 | or edx,0x00000003 114 | mov [ebx+esi*4],edx ;登记页的物理地址 115 | add eax,0x1000 ;下一个相邻页的物理地址 116 | inc esi 117 | cmp esi,256 ;仅低端1MB内存对应的页才是有效的 118 | jl .b1 119 | 120 | ;令CR3寄存器指向页目录,并正式开启页功能 121 | mov eax,0x00020000 ;PCD=PWT=0 122 | mov cr3,eax 123 | 124 | ;将GDT的线性地址映射到从0x80000000开始的相同位置 125 | sgdt [pgdt] 126 | mov ebx,[pgdt+2] 127 | add dword [pgdt+2],0x80000000 ;GDTR也用的是线性地址 128 | lgdt [pgdt] 129 | 130 | mov eax,cr0 131 | or eax,0x80000000 132 | mov cr0,eax ;开启分页机制 133 | 134 | ;将堆栈映射到高端,这是非常容易被忽略的一件事。应当把内核的所有东西 135 | ;都移到高端,否则,一定会和正在加载的用户任务局部空间里的内容冲突, 136 | ;而且很难想到问题会出在这里。 137 | add esp,0x80000000 138 | 139 | jmp [0x80040004] 140 | 141 | ;------------------------------------------------------------------------------- 142 | read_hard_disk_0: ;从硬盘读取一个逻辑扇区 143 | ;EAX=逻辑扇区号 144 | ;DS:EBX=目标缓冲区地址 145 | ;返回:EBX=EBX+512 146 | push eax 147 | push ecx 148 | push edx 149 | 150 | push eax 151 | 152 | mov dx,0x1f2 153 | mov al,1 154 | out dx,al ;读取的扇区数 155 | 156 | inc dx ;0x1f3 157 | pop eax 158 | out dx,al ;LBA地址7~0 159 | 160 | inc dx ;0x1f4 161 | mov cl,8 162 | shr eax,cl 163 | out dx,al ;LBA地址15~8 164 | 165 | inc dx ;0x1f5 166 | shr eax,cl 167 | out dx,al ;LBA地址23~16 168 | 169 | inc dx ;0x1f6 170 | shr eax,cl 171 | or al,0xe0 ;第一硬盘 LBA地址27~24 172 | out dx,al 173 | 174 | inc dx ;0x1f7 175 | mov al,0x20 ;读命令 176 | out dx,al 177 | 178 | .waits: 179 | in al,dx 180 | and al,0x88 181 | cmp al,0x08 182 | jnz .waits ;不忙,且硬盘已准备好数据传输 183 | 184 | mov ecx,256 ;总共要读取的字数 185 | mov dx,0x1f0 186 | .readw: 187 | in ax,dx 188 | mov [ebx],ax 189 | add ebx,2 190 | loop .readw 191 | 192 | pop edx 193 | pop ecx 194 | pop eax 195 | 196 | ret 197 | 198 | ;------------------------------------------------------------------------------- 199 | pgdt dw 0 200 | dd 0x00008000 ;GDT的物理/线性地址 201 | ;------------------------------------------------------------------------------- 202 | times 510-($-$$) db 0 203 | db 0x55,0xaa -------------------------------------------------------------------------------- /booktool/c17/c17_mbr_1.back: -------------------------------------------------------------------------------- 1 | ;代码清单17-1 2 | ;文件名:c17_mbr.asm 3 | ;文件说明:硬盘主引导扇区代码 4 | ;创建日期:2012-07-13 11:20 ;设置堆栈段和栈指针 5 | 6 | core_base_address equ 0x00040000 ;常数,内核加载的起始内存地址 7 | core_start_sector equ 0x00000001 ;常数,内核的起始逻辑扇区号 8 | 9 | ;=============================================================================== 10 | SECTION mbr vstart=0x00007c00 11 | 12 | mov ax,cs 13 | mov ss,ax 14 | mov sp,0x7c00 15 | 16 | ;计算GDT所在的逻辑段地址 17 | mov eax,[cs:pgdt+0x02] ;GDT的32位物理地址 18 | xor edx,edx 19 | mov ebx,16 20 | div ebx ;分解成16位逻辑地址 21 | 22 | mov ds,eax ;令DS指向该段以进行操作 23 | mov ebx,edx ;段内起始偏移地址 24 | 25 | ;跳过0#号描述符的槽位 26 | ;创建1#描述符,保护模式下的代码段描述符 27 | mov dword [ebx+0x08],0x0000ffff ;基地址为0,界限0xFFFFF,DPL=00 28 | mov dword [ebx+0x0c],0x00cf9800 ;4KB粒度,代码段描述符,向上扩展 29 | 30 | ;创建2#描述符,保护模式下的数据段和堆栈段描述符 31 | mov dword [ebx+0x10],0x0000ffff ;基地址为0,界限0xFFFFF,DPL=00 32 | mov dword [ebx+0x14],0x00cf9200 ;4KB粒度,数据段描述符,向上扩展 33 | 34 | ;初始化描述符表寄存器GDTR 35 | mov word [cs: pgdt],23 ;描述符表的界限 36 | 37 | lgdt [cs: pgdt] 38 | 39 | in al,0x92 ;南桥芯片内的端口 40 | or al,0000_0010B 41 | out 0x92,al ;打开A20 42 | 43 | cli ;中断机制尚未工作 44 | 45 | mov eax,cr0 46 | or eax,1 47 | mov cr0,eax ;设置PE位 48 | 49 | ;以下进入保护模式... ... 50 | jmp dword 0x0008:flush ;16位的描述符选择子:32位偏移 51 | ;清流水线并串行化处理器 52 | [bits 32] 53 | flush: 54 | mov eax,0x00010 ;加载数据段(4GB)选择子 55 | mov ds,eax 56 | mov es,eax 57 | mov fs,eax 58 | mov gs,eax 59 | mov ss,eax ;加载堆栈段(4GB)选择子 60 | mov esp,0x7000 ;堆栈指针 61 | 62 | ;以下加载系统核心程序 63 | mov edi,core_base_address 64 | 65 | mov eax,core_start_sector 66 | mov ebx,edi ;起始地址 67 | call read_hard_disk_0 ;以下读取程序的起始部分(一个扇区) 68 | 69 | ;以下判断整个程序有多大 70 | mov eax,[edi] ;核心程序尺寸 71 | xor edx,edx 72 | mov ecx,512 ;512字节每扇区 73 | div ecx 74 | 75 | or edx,edx 76 | jnz @1 ;未除尽,因此结果比实际扇区数少1 77 | dec eax ;已经读了一个扇区,扇区总数减1 78 | @1: 79 | or eax,eax ;考虑实际长度≤512个字节的情况 80 | jz pge ;EAX=0 ? 81 | 82 | ;读取剩余的扇区 83 | mov ecx,eax ;32位模式下的LOOP使用ECX 84 | mov eax,core_start_sector 85 | inc eax ;从下一个逻辑扇区接着读 86 | @2: 87 | call read_hard_disk_0 88 | inc eax 89 | loop @2 ;循环读,直到读完整个内核 90 | 91 | pge: 92 | ;准备打开分页机制。从此,再也不用在段之间转来转去,实在晕乎~ 93 | 94 | ;创建系统内核的页目录表PDT 95 | mov ebx,0x00020000 ;页目录表PDT的物理地址 96 | 97 | ;在页目录内创建指向页目录表自己的目录项 98 | mov dword [ebx+4092],0x00020003 99 | 100 | mov edx,0x00021003 ;MBR空间有限,后面尽量不使用立即数 101 | ;在页目录内创建与线性地址0x00000000对应的目录项 102 | mov [ebx+0x000],edx ;写入目录项(页表的物理地址和属性) 103 | ;此目录项仅用于过渡。 104 | ;在页目录内创建与线性地址0x80000000对应的目录项 105 | mov [ebx+0x800],edx ;写入目录项(页表的物理地址和属性) 106 | 107 | ;创建与上面那个目录项相对应的页表,初始化页表项 108 | mov ebx,0x00021000 ;页表的物理地址 109 | xor eax,eax ;起始页的物理地址 110 | xor esi,esi 111 | .b1: 112 | mov edx,eax 113 | or edx,0x00000003 114 | mov [ebx+esi*4],edx ;登记页的物理地址 115 | add eax,0x1000 ;下一个相邻页的物理地址 116 | inc esi 117 | cmp esi,256 ;仅低端1MB内存对应的页才是有效的 118 | jl .b1 119 | 120 | ;令CR3寄存器指向页目录,并正式开启页功能 121 | mov eax,0x00020000 ;PCD=PWT=0 122 | mov cr3,eax 123 | 124 | ;将GDT的线性地址映射到从0x80000000开始的相同位置 125 | sgdt [pgdt] 126 | mov ebx,[pgdt+2] 127 | add dword [pgdt+2],0x80000000 ;GDTR也用的是线性地址 128 | lgdt [pgdt] 129 | 130 | mov eax,cr0 131 | or eax,0x80000000 132 | mov cr0,eax ;开启分页机制 133 | 134 | ;将堆栈映射到高端,这是非常容易被忽略的一件事。应当把内核的所有东西 135 | ;都移到高端,否则,一定会和正在加载的用户任务局部空间里的内容冲突, 136 | ;而且很难想到问题会出在这里。 137 | add esp,0x80000000 138 | 139 | jmp [0x80040004] 140 | 141 | ;------------------------------------------------------------------------------- 142 | read_hard_disk_0: ;从硬盘读取一个逻辑扇区 143 | ;EAX=逻辑扇区号 144 | ;DS:EBX=目标缓冲区地址 145 | ;返回:EBX=EBX+512 146 | push eax 147 | push ecx 148 | push edx 149 | 150 | push eax 151 | 152 | mov dx,0x1f2 153 | mov al,1 154 | out dx,al ;读取的扇区数 155 | 156 | inc dx ;0x1f3 157 | pop eax 158 | out dx,al ;LBA地址7~0 159 | 160 | inc dx ;0x1f4 161 | mov cl,8 162 | shr eax,cl 163 | out dx,al ;LBA地址15~8 164 | 165 | inc dx ;0x1f5 166 | shr eax,cl 167 | out dx,al ;LBA地址23~16 168 | 169 | inc dx ;0x1f6 170 | shr eax,cl 171 | or al,0xe0 ;第一硬盘 LBA地址27~24 172 | out dx,al 173 | 174 | inc dx ;0x1f7 175 | mov al,0x20 ;读命令 176 | out dx,al 177 | 178 | .waits: 179 | in al,dx 180 | and al,0x88 181 | cmp al,0x08 182 | jnz .waits ;不忙,且硬盘已准备好数据传输 183 | 184 | mov ecx,256 ;总共要读取的字数 185 | mov dx,0x1f0 186 | .readw: 187 | in ax,dx 188 | mov [ebx],ax 189 | add ebx,2 190 | loop .readw 191 | 192 | pop edx 193 | pop ecx 194 | pop eax 195 | 196 | ret 197 | 198 | ;------------------------------------------------------------------------------- 199 | pgdt dw 0 200 | dd 0x00008000 ;GDT的物理/线性地址 201 | ;------------------------------------------------------------------------------- 202 | times 510-($-$$) db 0 203 | db 0x55,0xaa -------------------------------------------------------------------------------- /booktool/fixvhdwr.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/fixvhdwr.exe -------------------------------------------------------------------------------- /booktool/nasmide.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/nasmide.exe -------------------------------------------------------------------------------- /booktool/nasmide.ini: -------------------------------------------------------------------------------- 1 | [Options] 2 | Complier=C:\Program Files\NASM\nasm-2.10.04\nasm.exe 3 | -------------------------------------------------------------------------------- /booktool/~$越计卷2目录.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/~$越计卷2目录.doc -------------------------------------------------------------------------------- /booktool/相关文档/SB-16声卡播放程序/baby.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/相关文档/SB-16声卡播放程序/baby.wav -------------------------------------------------------------------------------- /booktool/相关文档/SB-16声卡播放程序/c10.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | SECTION header vstart=0 ;定义用户程序头部段 3 | program_length dd program_end ;程序总长度[0x00] 4 | 5 | ;用户程序入口点 6 | code_entry dw start ;偏移地址[0x04] 7 | dd section.code.start ;段地址[0x06] 8 | 9 | realloc_tbl_len dw (header_end-realloc_begin)/4 10 | ;段重定位表项个数[0x0a] 11 | 12 | realloc_begin: 13 | ;段重定位表 14 | code_segment dd section.code.start 15 | data_segment dd section.data.start 16 | stack_segment dd section.stack.start 17 | 18 | header_end: 19 | 20 | ;=============================================================================== 21 | SECTION code align=16 vstart=0 ;定义代码段(16字节对齐) 22 | put_string: ;显示字符串(0结尾) 23 | ;输入:DS:BX=串地址 24 | push ax 25 | push bx 26 | push si 27 | 28 | mov ah,0x0e ;INT 0x10第0x0e号功能 29 | mov si,bx ;字符串起始偏移地址 30 | mov bl,0x07 ;显示属性 31 | 32 | .gchr: 33 | mov al,[si] ;逐个取要显示的字符 34 | or al,al ;如果AL内容为零,则 35 | jz .rett ;跳转到过程返回指令 36 | int 0x10 ;BIOS字符显示功能调用 37 | inc si ;下一个字符 38 | jmp .gchr 39 | 40 | .rett: 41 | pop si 42 | pop bx 43 | pop ax 44 | 45 | ret 46 | 47 | ;------------------------------------------------------------------------------- 48 | write_dsp: 49 | push dx 50 | push ax 51 | 52 | mov dx,022ch 53 | .@22c: 54 | in al,dx 55 | and al,1000_0000b ;监视22c端口的第7位,直到它变为0 56 | jnz .@22c 57 | 58 | pop ax 59 | out dx,al 60 | pop dx 61 | 62 | ret 63 | 64 | ;------------------------------------------------------------------------------- 65 | read_dsp: 66 | push dx 67 | 68 | mov dx,22eh 69 | .@22e: 70 | in al,dx 71 | and al,1000_0000b ;监视22e端口的位7,直到它变成1 72 | jz .@22e 73 | mov dx,22ah 74 | in al,dx ;此时可以从22a端口读取数据 75 | 76 | pop dx 77 | 78 | ret 79 | 80 | ;------------------------------------------------------------------------------- 81 | start: 82 | mov ax,[stack_segment] 83 | mov ss,ax ;修改SS时,处理器将在执行下一指 84 | mov sp,ss_pointer ;令前禁止中断 85 | 86 | mov ax,[data_segment] 87 | mov ds,ax 88 | 89 | mov bx,init_msg 90 | call put_string 91 | 92 | ;以下复位DSP芯片 93 | mov dx,0x226 94 | mov al,1 ;第一步,先写“1”到复位端口 95 | out dx,al 96 | 97 | xor ax,ax 98 | .@1: 99 | dec ax 100 | jnz .@1 ;一个硬件要求的延时(至少3ms) 101 | 102 | out dx,al ;第二步,写“0”到复位端口 103 | 104 | call read_dsp 105 | cmp al,0xaa ;状态值0xaa表示初始化完成 106 | jz .@4 107 | 108 | mov bx,err_msg ;显示出错信息 109 | call put_string 110 | jmp .idle ;直接停机 111 | 112 | .@4: 113 | mov bx,done_msg 114 | call put_string 115 | 116 | ;以下安装中断处理过程 117 | mov bx,intr_msg 118 | call put_string 119 | 120 | mov al,0x0d ;IR5引脚的中断号 121 | mov bl,4 ;每个中断向量占4个字节。本段等效于: 122 | mul bl ;mov bx,0x0d 123 | mov bx,ax ;shl bx,2 124 | 125 | cli ;禁止在修改IVT期间发生中断 126 | 127 | push es ;临时使用ES 128 | xor ax,ax 129 | mov es,ax ;指向内存最低处的中断向量表 130 | mov word [es:bx],dsp_interrupt 131 | ;偏移地址 132 | inc bx 133 | inc bx 134 | mov word [es:bx],cs ;当前代码段 135 | pop es 136 | 137 | sti 138 | 139 | ;允许IRQ5 140 | in al,0x21 ;8259主片的IMR 141 | and al,1101_1111B ;开放IR5 142 | out 0x21,al 143 | 144 | mov bx,done_msg 145 | call put_string 146 | 147 | mov bx,dma_msg 148 | call put_string 149 | 150 | ;对DMA控制器编程,设置其工作模式、缓冲区地址和传输长度 151 | mov dx,0x0a ;DMAC1的屏蔽寄存器 152 | mov al,00000_1_01B ;关闭主DMAC的1号通道 153 | out dx,al 154 | 155 | mov ax,ds ;计算缓冲区物理地址 156 | mov bx,16 157 | mul bx 158 | add ax,voice_data 159 | adc dx,0 160 | mov bx,dx ;bx:ax=缓冲区20位地址 161 | 162 | xor al,al 163 | out 0x0c,al ;DMAC1高低触发器清零 164 | 165 | mov dx,0x02 ;写通道1基址与当前地址寄存器 166 | out dx,al ;低8位DMA地址 167 | mov al,ah 168 | out dx,al ;高8位DMA地址 169 | 170 | mov dx,0x83 ;写DMA通道 1 的页面寄存器 171 | mov al,bl 172 | out dx,al 173 | 174 | mov dx,0x03 ;写通道1的基字计数与当前字计数器 175 | mov ax,init_msg-voice_data ;数据块(当缓冲区用)的大小 176 | dec ax ;DMA要求实际大小减一 177 | out dx,al ;缓冲区长度低8位 178 | mov al,ah 179 | out dx,al ;缓冲区长度高8位 180 | 181 | mov al,0101_1001b ;设置DMAC1通道1工作方式:单字节传送/ 182 | out 0x0b,al ;地址递增/自动预置/读传送/通道1 183 | 184 | mov dx,0x0a ;DMAC1屏蔽寄存器 185 | mov al,1 ;允许通道1接受请求 186 | out dx,al 187 | 188 | mov al,0x40 ;设置DSP采样率(播放) 189 | call write_dsp 190 | mov ax,65536-(256000000/(1*8000)) 191 | xchg ah,al ;只使用结果的高8位 192 | call write_dsp 193 | 194 | ;显示信息 195 | mov bx,done_msg 196 | call put_string 197 | 198 | ;编程设置DSP的DMA传输模式和数据长度,以启动音频播放 199 | mov al,0x48 200 | call write_dsp 201 | mov ax,init_msg-voice_data ;数据块(当缓冲区用)的大小 202 | shr ax,1 ;长度设为DMA的一半 203 | dec ax 204 | call write_dsp ;写低字节 205 | xchg ah,al 206 | call write_dsp ;写高字节 207 | 208 | ;打开喇叭输出 209 | mov al,0xd1 210 | call write_dsp 211 | 212 | ;启动DSP的传输的播放 213 | mov al,0x1c 214 | call write_dsp 215 | 216 | mov bx,play_msg 217 | call put_string 218 | 219 | .idle: 220 | hlt 221 | jmp .idle 222 | 223 | ;------------------------------------------------------------------------------- 224 | dsp_interrupt: ;中断处理过程 225 | push ax 226 | push bx 227 | push dx 228 | 229 | ;退出自动初始化模式 230 | mov al,0xda 231 | call write_dsp 232 | 233 | ;关闭扬声器 234 | mov al,0xd3 235 | call write_dsp 236 | 237 | mov bx,done_msg 238 | call put_string 239 | 240 | mov bx,okay_msg 241 | call put_string 242 | 243 | mov dx,0x22f ;DSP中断应答 244 | in al,dx 245 | 246 | ;发送EOI命令到中断控制器(主片) 247 | mov al,0x20 ;中断结束命令EOI 248 | out 0x20,al ;发给主片 249 | 250 | pop dx 251 | pop bx 252 | pop ax 253 | 254 | iret 255 | 256 | ;------------------------------------------------------------------------------- 257 | SECTION data align=16 vstart=0 258 | 259 | voice_data incbin "baby.wav",44 260 | 261 | init_msg db 'Initializing sound blaster card...',0 262 | 263 | intr_msg db 'Installing interrupt vector...',0 264 | 265 | dma_msg db 'Setup DMA ...',0 266 | 267 | done_msg db 'Done.',0x0d,0x0a,0 268 | 269 | play_msg db 'Voice is playing now...',0 270 | 271 | okay_msg db 'Finished,stop.',0 272 | 273 | err_msg db 'Sound card init failed.',0 274 | 275 | ;=============================================================================== 276 | SECTION stack align=16 vstart=0 277 | 278 | resb 256 279 | ss_pointer: 280 | 281 | ;=============================================================================== 282 | SECTION program_trail 283 | program_end: -------------------------------------------------------------------------------- /booktool/相关文档/SB-16声卡播放程序/c10.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/相关文档/SB-16声卡播放程序/c10.bin -------------------------------------------------------------------------------- /booktool/相关文档/SB-16声卡播放程序/c10.lst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/相关文档/SB-16声卡播放程序/c10.lst -------------------------------------------------------------------------------- /booktool/相关文档/SB-16声卡播放程序/声霸卡硬件开发手册[英文].pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/相关文档/SB-16声卡播放程序/声霸卡硬件开发手册[英文].pdf -------------------------------------------------------------------------------- /booktool/相关文档/Virtual Hard Disk Format Spec_10_18_06.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/相关文档/Virtual Hard Disk Format Spec_10_18_06.doc -------------------------------------------------------------------------------- /booktool/相关文档/原稿第10章/baby.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/相关文档/原稿第10章/baby.wav -------------------------------------------------------------------------------- /booktool/相关文档/原稿第10章/c10.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | SECTION header vstart=0 ;定义用户程序头部段 3 | program_length dd program_end ;程序总长度[0x00] 4 | 5 | ;用户程序入口点 6 | code_entry dw start ;偏移地址[0x04] 7 | dd section.code.start ;段地址[0x06] 8 | 9 | realloc_tbl_len dw (header_end-realloc_begin)/4 10 | ;段重定位表项个数[0x0a] 11 | 12 | realloc_begin: 13 | ;段重定位表 14 | code_segment dd section.code.start 15 | data_segment dd section.data.start 16 | stack_segment dd section.stack.start 17 | 18 | header_end: 19 | 20 | ;=============================================================================== 21 | SECTION code align=16 vstart=0 ;定义代码段(16字节对齐) 22 | put_string: ;显示字符串(0结尾) 23 | ;输入:DS:BX=串地址 24 | push ax 25 | push bx 26 | push si 27 | 28 | mov ah,0x0e ;INT 0x10第0x0e号功能 29 | mov si,bx ;字符串起始偏移地址 30 | mov bl,0x07 ;显示属性 31 | 32 | .gchr: 33 | mov al,[si] ;逐个取要显示的字符 34 | or al,al ;如果AL内容为零,则 35 | jz .rett ;跳转到过程返回指令 36 | int 0x10 ;BIOS字符显示功能调用 37 | inc si ;下一个字符 38 | jmp .gchr 39 | 40 | .rett: 41 | pop si 42 | pop bx 43 | pop ax 44 | 45 | ret 46 | 47 | ;------------------------------------------------------------------------------- 48 | write_dsp: 49 | push dx 50 | push ax 51 | 52 | mov dx,022ch 53 | .@22c: 54 | in al,dx 55 | and al,1000_0000b ;监视22c端口的第7位,直到它变为0 56 | jnz .@22c 57 | 58 | pop ax 59 | out dx,al 60 | pop dx 61 | 62 | ret 63 | 64 | ;------------------------------------------------------------------------------- 65 | read_dsp: 66 | push dx 67 | 68 | mov dx,22eh 69 | .@22e: 70 | in al,dx 71 | and al,1000_0000b ;监视22e端口的位7,直到它变成1 72 | jz .@22e 73 | mov dx,22ah 74 | in al,dx ;此时可以从22a端口读取数据 75 | 76 | pop dx 77 | 78 | ret 79 | 80 | ;------------------------------------------------------------------------------- 81 | start: 82 | mov ax,[stack_segment] 83 | mov ss,ax ;修改SS时,处理器将在执行下一指 84 | mov sp,ss_pointer ;令前禁止中断 85 | 86 | mov ax,[data_segment] 87 | mov ds,ax 88 | 89 | mov bx,init_msg 90 | call put_string 91 | 92 | ;以下复位DSP芯片 93 | mov dx,0x226 94 | mov al,1 ;第一步,先写“1”到复位端口 95 | out dx,al 96 | 97 | xor ax,ax 98 | .@1: 99 | dec ax 100 | jnz .@1 ;一个硬件要求的延时(至少3ms) 101 | 102 | out dx,al ;第二步,写“0”到复位端口 103 | 104 | call read_dsp 105 | cmp al,0xaa ;状态值0xaa表示初始化完成 106 | jz .@4 107 | 108 | mov bx,err_msg ;显示出错信息 109 | call put_string 110 | jmp .idle ;直接停机 111 | 112 | .@4: 113 | mov bx,done_msg 114 | call put_string 115 | 116 | ;以下安装中断处理过程 117 | mov bx,intr_msg 118 | call put_string 119 | 120 | mov al,0x0d ;IR5引脚的中断号 121 | mov bl,4 ;每个中断向量占4个字节。本段等效于: 122 | mul bl ;mov bx,0x0d 123 | mov bx,ax ;shl bx,2 124 | 125 | cli ;禁止在修改IVT期间发生中断 126 | 127 | push es ;临时使用ES 128 | xor ax,ax 129 | mov es,ax ;指向内存最低处的中断向量表 130 | mov word [es:bx],dsp_interrupt 131 | ;偏移地址 132 | inc bx 133 | inc bx 134 | mov word [es:bx],cs ;当前代码段 135 | pop es 136 | 137 | sti 138 | 139 | ;允许IRQ5 140 | in al,0x21 ;8259主片的IMR 141 | and al,1101_1111B ;开放IR5 142 | out 0x21,al 143 | 144 | mov bx,done_msg 145 | call put_string 146 | 147 | mov bx,dma_msg 148 | call put_string 149 | 150 | ;对DMA控制器编程,设置其工作模式、缓冲区地址和传输长度 151 | mov dx,0x0a ;DMAC1的屏蔽寄存器 152 | mov al,00000_1_01B ;关闭主DMAC的1号通道 153 | out dx,al 154 | 155 | mov ax,ds ;计算缓冲区物理地址 156 | mov bx,16 157 | mul bx 158 | add ax,voice_data 159 | adc dx,0 160 | mov bx,dx ;bx:ax=缓冲区20位地址 161 | 162 | xor al,al 163 | out 0x0c,al ;DMAC1高低触发器清零 164 | 165 | mov dx,0x02 ;写通道1基址与当前地址寄存器 166 | out dx,al ;低8位DMA地址 167 | mov al,ah 168 | out dx,al ;高8位DMA地址 169 | 170 | mov dx,0x83 ;写DMA通道 1 的页面寄存器 171 | mov al,bl 172 | out dx,al 173 | 174 | mov dx,0x03 ;写通道1的基字计数与当前字计数器 175 | mov ax,init_msg-voice_data ;数据块(当缓冲区用)的大小 176 | dec ax ;DMA要求实际大小减一 177 | out dx,al ;缓冲区长度低8位 178 | mov al,ah 179 | out dx,al ;缓冲区长度高8位 180 | 181 | mov al,0101_1001b ;设置DMAC1通道1工作方式:单字节传送/ 182 | out 0x0b,al ;地址递增/自动预置/读传送/通道1 183 | 184 | mov dx,0x0a ;DMAC1屏蔽寄存器 185 | mov al,1 ;允许通道1接受请求 186 | out dx,al 187 | 188 | mov al,0x40 ;设置DSP采样率(播放) 189 | call write_dsp 190 | mov ax,65536-(256000000/(1*8000)) 191 | xchg ah,al ;只使用结果的高8位 192 | call write_dsp 193 | 194 | ;显示信息 195 | mov bx,done_msg 196 | call put_string 197 | 198 | ;编程设置DSP的DMA传输模式和数据长度,以启动音频播放 199 | mov al,0x48 200 | call write_dsp 201 | mov ax,init_msg-voice_data ;数据块(当缓冲区用)的大小 202 | shr ax,1 ;长度设为DMA的一半 203 | dec ax 204 | call write_dsp ;写低字节 205 | xchg ah,al 206 | call write_dsp ;写高字节 207 | 208 | ;打开喇叭输出 209 | mov al,0xd1 210 | call write_dsp 211 | 212 | ;启动DSP的传输的播放 213 | mov al,0x1c 214 | call write_dsp 215 | 216 | mov bx,play_msg 217 | call put_string 218 | 219 | .idle: 220 | hlt 221 | jmp .idle 222 | 223 | ;------------------------------------------------------------------------------- 224 | dsp_interrupt: ;中断处理过程 225 | push ax 226 | push bx 227 | push dx 228 | 229 | ;退出自动初始化模式 230 | mov al,0xda 231 | call write_dsp 232 | 233 | ;关闭扬声器 234 | mov al,0xd3 235 | call write_dsp 236 | 237 | mov bx,done_msg 238 | call put_string 239 | 240 | mov bx,okay_msg 241 | call put_string 242 | 243 | mov dx,0x22f ;DSP中断应答 244 | in al,dx 245 | 246 | ;发送EOI命令到中断控制器(主片) 247 | mov al,0x20 ;中断结束命令EOI 248 | out 0x20,al ;发给主片 249 | 250 | pop dx 251 | pop bx 252 | pop ax 253 | 254 | iret 255 | 256 | ;------------------------------------------------------------------------------- 257 | SECTION data align=16 vstart=0 258 | 259 | voice_data incbin "baby.wav",44 260 | 261 | init_msg db 'Initializing sound blaster card...',0 262 | 263 | intr_msg db 'Installing interrupt vector...',0 264 | 265 | dma_msg db 'Setup DMA ...',0 266 | 267 | done_msg db 'Done.',0x0d,0x0a,0 268 | 269 | play_msg db 'Voice is playing now...',0 270 | 271 | okay_msg db 'Finished,stop.',0 272 | 273 | err_msg db 'Sound card init failed.',0 274 | 275 | ;=============================================================================== 276 | SECTION stack align=16 vstart=0 277 | 278 | resb 256 279 | ss_pointer: 280 | 281 | ;=============================================================================== 282 | SECTION program_trail 283 | program_end: -------------------------------------------------------------------------------- /booktool/相关文档/原稿第10章/c10.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/相关文档/原稿第10章/c10.bin -------------------------------------------------------------------------------- /booktool/相关文档/原稿第10章/c10.lst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/相关文档/原稿第10章/c10.lst -------------------------------------------------------------------------------- /booktool/相关文档/原稿第10章/声霸卡硬件开发手册[英文].pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/相关文档/原稿第10章/声霸卡硬件开发手册[英文].pdf -------------------------------------------------------------------------------- /booktool/相关文档/原稿第10章/第十章 聆听数字的声音.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/booktool/相关文档/原稿第10章/第十章 聆听数字的声音.doc -------------------------------------------------------------------------------- /x86汇编语言-从实模式到保护模式.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liracle/codeOfAssembly/3c3900e8b1c297ed91f89342612d43f64f8c095b/x86汇编语言-从实模式到保护模式.pdf -------------------------------------------------------------------------------- /操作系统启动过程.md: -------------------------------------------------------------------------------- 1 | 这篇文章记录操作系统启动引导过程 2 | 3 | # 操作系统的运行模式 4 | 80386开始,cpu有三种工作方式:实模式,保护模式和虚拟8086模式。实模式是早起intel cpu(80286及之前)的工作模式,20根地址线,可访问内存为1MB。没有内存保护和多任务等概念。当前系统都不在工作实模式下,但是开机时还是需要通过实模式加载和引导到保护模式的(因为开机启动时执行的BIOS指令是16位模式,工作在实模式下)。 5 | 6 | # 操作系统启动过程简介 7 | 1. cpu引脚中有一个是RESET,每次加电,处理器都会执行一个硬件初始化,将内部所有寄存器内容初始化到一个预置状态。对于Intel 8086,设置cs:ip为0xffff:0000,其他寄存器全为0,根据下面的实模式内存布局图可以看到该内存区域为BIOS入口地址(在Intel 8086中,BIOS ROM占据着整个内存空间顶端的64KB,物理地址范围是0xf0000~0xfffff), BIOS ROM中在物理地址0xffff0的地方,一般是一个跳转指令,比如`jmp 0xf000:0xe05b`,在这里,BIOS将执行一些工作,例如硬件的诊断、检测、和初始化,让硬件处于一个正常的默认的工作状态,最后要做的一件事情是从外存储设备读取数据进行加载,具体内容:从设置的系统引导设备顺序一一检查每个盘第一个扇区(512字节)的最后两个字节是否是0x55aa,如果是,则认定是一个启动区,否则继续检查下一个引导设备的第一个扇区,直到找到这样一个扇区为止。找到这样一个扇区后,中断程序将对应的512个字节数据拷贝到地址为0x7c00处(不纠结这个为啥0x7c00,历史就这么约定的),然后将对应的cs:ip 设置为 **0x0000:7c00**,注意在执行主引导记录程序时cs是0x0000,不是0x07c0,因此再编写汇编时,段起始地址是0。 8 | 9 | **实模式下内存布局** 10 | 11 | |起始地址|结束地址|大小|用途| 12 | |:--:|:--:|:--:|:--:| 13 | |FFFF0|FFFFF|16B|BIOS入口地址,此地址也属于BIOS区域| 14 | |F0000|FFFEF|64KB-16B|BIOS区域| 15 | |C8000|EFFFF|160KB|映射硬件适配器的ROM或内存映射式I/O| 16 | |C0000|C7FFF|32KB|显示器适配BIOS| 17 | |B8000|BFFFF|32KB|用于文本模式显示适配器| 18 | |B0000|B7FFF|32KB|用于黑白显示器适配器| 19 | |A0000|AFFFF|64KB|用于彩色显示器适配器| 20 | |9FC00|9FFFF|1KB|EBDA,扩展BIOS区域| 21 | |07E00|9FBFF|约608KB|可用区域| 22 | |07C00|07DFF|512B|MBR被BIOS加载到此处| 23 | |00500|07BFF|约30KB|可用区域| 24 | |00400|004FF|256B|BIOS Data Area (BIOS数据区)| 25 | |00000|003FF|1KB|interrupt Vector Table(中断向量表)| 26 | 27 | 2. 步骤1中设置cs:ip后,接下来cpu就应该从0x7c00处执行代码,此部分代码数据就是我们在镜像中对应的前512个字节数据,这512个字节我们称之为MBR扇区,一般在MBR中实现操作系统代码的加载(完成操作系统代码从镜像文件到操作系统的拷贝)。在我们最开始的时候,我们只是在第一个扇区中执行了打印字符串功能(BIOS提供的中断程序能力)。 28 | 3. 在MBR扇区中,执行数据拷贝,将镜像文件中的操作系统数据拷贝到内存中,然后再设置cs:ip,跳转到对应的操作系统代码起始点,此时进入操作系统代码执行 29 | 4. 汇编语言中,关于标号不得不说的事情 30 | 在写主引导区程序时,关于标号,我常常会陷入迷惑中,不知道标号代表的是该指令对应的段地址,还是偏移地址,该偏移地址又是指啥 31 | 首先可以确定的是,肯定不是段地址,段地址对应了一个很大的范围,不能精确定位到某条指令; 32 | 其次该指令确实是代表的偏移地址,但是该偏移地址是多少就要分两种情况了: 33 | * 假如在程序头部声明了org 标号,指明了程序被装在到内存中的地址,那么标号代表的地址就是该指令的地址-第一个指令的地址+org表示的其实地址 34 | * 如果程序没有声明org标号,那么标号代表的地址就是该指令的地址-第一个指令的地址 35 | 5. 由4引发的对$,$$符号的思考 36 | 37 | 38 | --------------------------------------------------------------------------------