c05_mbr.asm
;代码清单5-1
;文件名:c05_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-3-31 21:15
mov ax,0xb800 ;指向文本模式的显示缓冲区
mov es,ax
;以下显示字符串“Label offset:”
mov byte [es:0x00],‘L’
mov byte [es:0x01],0x07
mov byte [es:0x02],‘a’
mov byte [es:0x03],0x07
mov byte [es:0x04],‘b’
mov byte [es:0x05],0x07
mov byte [es:0x06],‘e’
mov byte [es:0x07],0x07
mov byte [es:0x08],‘l’
mov byte [es:0x09],0x07
mov byte [es:0x0a],‘ ’
mov byte [es:0x0b],0x07
mov byte [es:0x0c],“o”
mov byte [es:0x0d],0x07
mov byte [es:0x0e],‘f’
mov byte [es:0x0f],0x07
mov byte [es:0x10],‘f’
mov byte [es:0x11],0x07
mov byte [es:0x12],‘s’
mov byte [es:0x13],0x07
mov byte [es:0x14],‘e’
mov byte [es:0x15],0x07
mov byte [es:0x16],‘t’
mov byte [es:0x17],0x07
mov byte [es:0x18],‘:’
mov byte [es:0x19],0x07
mov ax,number ;取得标号number的偏移地址
mov bx,10
;设置数据段的基地址
mov cx,cs
mov ds,cx
;求个位上的数字
mov dx,0
div bx
mov [0x7c00+number+0x00],dl ;保存个位上的数字
;求十位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x01],dl ;保存十位上的数字
;求百位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x02],dl ;保存百位上的数字
;求千位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x03],dl ;保存千位上的数字
;求万位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x04],dl ;保存万位上的数字
;以下用十进制显示标号的偏移地址
mov al,[0x7c00+number+0x04]
add al,0x30
mov [es:0x1a],al
mov byte [es:0x1b],0x04
mov al,[0x7c00+number+0x03]
add al,0x30
mov [es:0x1c],al
mov byte [es:0x1d],0x04
mov al,[0x7c00+number+0x02]
add al,0x30
mov [es:0x1e],al
mov byte [es:0x1f],0x04
mov al,[0x7c00+number+0x01]
add al,0x30
mov [es:0x20],al
mov byte [es:0x21],0x04
mov al,[0x7c00+number+0x00]
add al,0x30
mov [es:0x22],al
mov byte [es:0x23],0x04
mov byte [es:0x24],‘D’
mov byte [es:0x25],0x07
infi: jmp near infi ;无限循环
number db 0,0,0,0,0
times 203 db 0
db 0x55,0xaa
c06_mbr.asm
;代码清单6-1
;文件名:c06_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-4-12 22:12
jmp near start
mytext db ‘L’,0x07,‘a’,0x07,‘b’,0x07,‘e’,0x07,‘l’,0x07,‘ ’,0x07,‘o’,0x07,
‘f’,0x07,‘f’,0x07,‘s’,0x07,‘e’,0x07,‘t’,0x07,‘:’,0x07
number db 0,0,0,0,0
start:
mov ax,0x07c0 ;设置数据段基地址
mov ds,ax ;ds寄存器一般保存数据段基地址
mov ax,0xb800 ;设置附加段基地址
mov es,ax ;这里附加段指向显存位置,存放在es寄存器中
cld ;将方向标志位DF清零,以指示传送是负方向的,与此相对应的指令是std
mov si,mytext ;movsw指令原始数据串需要存放在ds:si位置,目的地址为es:di,因为ds目前指示的是当前代码段基地址地址,因此只要把偏移mytext存入si寄存器即可
mov di,0 ;当前es指示显存起始位置,因此只要把偏移0存入di即可
mov cx,(number-mytext)/2 ;实际上等于 13,cx作为计数器,每进行一次rep指令cx-1
rep movsw ;一次传送一个字(两个字节)
;得到标号所代表的偏移地址
mov ax,number ;此代码目的旨在显示number的偏移地址
;计算各个数位
mov bx,ax ;bx指向当前number偏移地址
mov cx,5 ;循环次数
mov si,10 ;除数
digit:
xor dx,dx ;dx(被除数高16位)清零
div si ;除法
mov [bx],dl ;保存数位 ;为什么这里不用加0x7c00了?
inc bx ;bx自加1,指向下一个内存单元,number一共定义了5个字节内存单元
loop digit
;显示各个数位
mov bx,number
mov si,4
show:
mov al,[bx+si] ;从后往前显示
add al,0x30
mov ah,0x04
mov [es:di],ax
add di,2
dec si
jns show ;上一条指令符号位为SF=0(结果为非负)时跳转
mov word [es:di],0x0744
jmp near $ ;无限循环
times 510-($-$$) db 0 ;512字节减去之后两个db指令=510字节,$当前指令偏移,$$当前代码段起始位置,填充字节0(db 0)
db 0x55,0xaa
c07_mbr.asm
;代码清单7-1
;文件名:c07_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-4-13 18:02
jmp near start
message db ‘1+2+3+.。.+100=’
start:
mov ax,0x7c0 ;设置数据段的段基地址
mov ds,ax
mov ax,0xb800 ;设置附加段基址到显示缓冲区
mov es,ax
;以下显示字符串
mov si,message
mov di,0
mov cx,start-message
@g:
mov al,[si] ;因为这是硬盘主引导扇区代码,因此被加载到0x7c00,[si]=[ds:si],就是相对于代码段开头的相对偏移,这个相对偏移就是标签message的值
mov [es:di],al
inc di ;di用做显存段地址的相对偏移,字符内容信息放在低一个字节
mov byte [es:di],0x07
inc di ;字符显示信息放在高一个字节
inc si ;si用作寻址字符串相对偏移
loop @g
;以下计算1到100的和
xor ax,ax ;清空ax寄存器,存放结果
mov cx,1
@f:
add ax,cx
inc cx ;cx做累加器
cmp cx,100
jle @f ;小于等于时跳转
;以下计算累加和的每个数位
xor cx,cx ;设置堆栈段的段基地址
mov ss,cx
mov sp,cx ;堆栈段指针和段基址都在0x0000处,堆栈段从高地址向低地址生长
mov bx,10
xor cx,cx
@d:
inc cx ;压栈中用cx记录一共压入栈元素个数,以便之后出栈时能及时停止pop
xor dx,dx ;被除数[dx:ax]
div bx ;除数bx
or dl,0x30 ;余数在dx中,但是余数最多到9,因此在dl中就够了,加0x30得到ASCII码
push dx ;dx中只有dl有意义,但是压栈的单位必须是字(两个字节)
cmp ax,0
jne @d ;循环跳出时,结果5050每一位被放在栈中
;以下显示各个数位
@a:
pop dx ;出栈,栈顶元素是千位,百位,十位,个位
mov [es:di],dl
inc di
mov byte [es:di],0x07
inc di
loop @a
jmp near $
8086寻址方式总结:
1.寄存器寻址 :mov ax,cx
2.立即寻址 :add bx,0xf000 (目的操作数立即数寻址,源操作数寄存器寻址)
3.直接寻址 :mov ax,[0x5c0f]
4.基址寻址:利用基址寄存器bx/bp中的值作为偏移地址,其中bx默认段寄存器为ds(数据段),bp默认段寄存器为ss(堆栈段)。堆栈段指针寄存器为sp,不要混淆。
如:mov dx,[bp-2]
5.变址寻址:利用变址寄存器si/di中的值作为偏移地址,默认段寄存器为ds
如:mov [si+0x100],al
6.基址变址寻址:利用基址寄存器bx/bp和变址寄存器si/di相加作为偏移地址
如:add word [bx+di],0x3000
加载程序 源程序c08_mbr.asm
;代码清单8-1
;文件名:c08_mbr.asm
;文件说明:硬盘主引导扇区代码(加载程序)
;创建日期:2011-5-5 18:17
app_lba_start equ 100 ;声明常数(用户程序起始逻辑扇区号)
;常数的声明不会占用汇编地址
SECTION mbr align=16 vstart=0x7c00
;设置堆栈段和栈指针
mov ax,0
mov ss,ax
mov sp,ax
mov ax,[cs:phy_base] ;计算用于加载用户程序的逻辑段地址
mov dx,[cs:phy_base+0x02]
mov bx,16
div bx
mov ds,ax ;令DS和ES指向该段以进行操作
mov es,ax
;以下读取程序的起始部分
xor di,di
mov si,app_lba_start ;程序在硬盘上的起始逻辑扇区号
xor bx,bx ;加载到DS:0x0000处
call read_hard_disk_0
;以下判断整个程序有多大
mov dx,[2] ;曾经把dx写成了ds,花了二十分钟排错
mov ax,[0]
mov bx,512 ;512字节每扇区
div bx
cmp dx,0
jnz @1 ;未除尽,因此结果比实际扇区数少1
dec ax ;已经读了一个扇区,扇区总数减1
@1:
cmp ax,0 ;考虑实际长度小于等于512个字节的情况
jz direct
;读取剩余的扇区
push ds ;以下要用到并改变DS寄存器
mov cx,ax ;循环次数(剩余扇区数)
@2:
mov ax,ds
add ax,0x20 ;得到下一个以512字节为边界的段地址
mov ds,ax
xor bx,bx ;每次读时,偏移地址始终为0x0000
inc si ;下一个逻辑扇区
call read_hard_disk_0
loop @2 ;循环读,直到读完整个功能程序
pop ds ;恢复数据段基址到用户程序头部段
;计算入口点代码段基址
direct:
mov dx,[0x08]
mov ax,[0x06]
call calc_segment_base
mov [0x06],ax ;回填修正后的入口点代码段基址
;开始处理段重定位表
mov cx,[0x0a] ;需要重定位的项目数量
mov bx,0x0c ;重定位表首地址
realloc:
mov dx,[bx+0x02] ;32位地址的高16位
mov ax,[bx]
call calc_segment_base
mov [bx],ax ;回填段的基址
add bx,4 ;下一个重定位项(每项占4个字节)
loop realloc
jmp far [0x04] ;转移到用户程序
;-------------------------------------------------------------------------------
read_hard_disk_0: ;从硬盘读取一个逻辑扇区
;输入:DI:SI=起始逻辑扇区号
; DS:BX=目标缓冲区地址
push ax
push bx
push cx
push dx
mov dx,0x1f2
mov al,1
out dx,al ;读取的扇区数
inc dx ;0x1f3
mov ax,si
out dx,al ;LBA地址7~0
inc dx ;0x1f4
mov al,ah
out dx,al ;LBA地址15~8
inc dx ;0x1f5
mov ax,di
out dx,al ;LBA地址23~16
inc dx ;0x1f6
mov al,0xe0 ;LBA28模式,主盘
or al,ah ;LBA地址27~24
out dx,al
inc dx ;0x1f7
mov al,0x20 ;读命令
out dx,al
.waits:
in al,dx
and al,0x88
cmp al,0x08
jnz .waits ;不忙,且硬盘已准备好数据传输
mov cx,256 ;总共要读取的字数
mov dx,0x1f0
.readw:
in ax,dx
mov [bx],ax
add bx,2
loop .readw
pop dx
pop cx
pop bx
pop ax
ret
;-------------------------------------------------------------------------------
calc_segment_base: ;计算16位段地址
;输入:DX:AX=32位物理地址
;返回:AX=16位段基地址
push dx
add ax,[cs:phy_base]
adc dx,[cs:phy_base+0x02]
shr ax,4
ror dx,4
and dx,0xf000
or ax,dx
pop dx
ret
;-------------------------------------------------------------------------------
phy_base dd 0x10000 ;用户程序被加载的物理起始地址
times 510-($-$$) db 0
db 0x55,0xaa
c05_mbr.asm
;代码清单5-1
;文件名:c05_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-3-31 21:15
mov ax,0xb800 ;指向文本模式的显示缓冲区
mov es,ax
;以下显示字符串“Label offset:”
mov byte [es:0x00],‘L’
mov byte [es:0x01],0x07
mov byte [es:0x02],‘a’
mov byte [es:0x03],0x07
mov byte [es:0x04],‘b’
mov byte [es:0x05],0x07
mov byte [es:0x06],‘e’
mov byte [es:0x07],0x07
mov byte [es:0x08],‘l’
mov byte [es:0x09],0x07
mov byte [es:0x0a],‘ ’
mov byte [es:0x0b],0x07
mov byte [es:0x0c],“o”
mov byte [es:0x0d],0x07
mov byte [es:0x0e],‘f’
mov byte [es:0x0f],0x07
mov byte [es:0x10],‘f’
mov byte [es:0x11],0x07
mov byte [es:0x12],‘s’
mov byte [es:0x13],0x07
mov byte [es:0x14],‘e’
mov byte [es:0x15],0x07
mov byte [es:0x16],‘t’
mov byte [es:0x17],0x07
mov byte [es:0x18],‘:’
mov byte [es:0x19],0x07
mov ax,number ;取得标号number的偏移地址
mov bx,10
;设置数据段的基地址
mov cx,cs
mov ds,cx
;求个位上的数字
mov dx,0
div bx
mov [0x7c00+number+0x00],dl ;保存个位上的数字
;求十位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x01],dl ;保存十位上的数字
;求百位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x02],dl ;保存百位上的数字
;求千位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x03],dl ;保存千位上的数字
;求万位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x04],dl ;保存万位上的数字
;以下用十进制显示标号的偏移地址
mov al,[0x7c00+number+0x04]
add al,0x30
mov [es:0x1a],al
mov byte [es:0x1b],0x04
mov al,[0x7c00+number+0x03]
add al,0x30
mov [es:0x1c],al
mov byte [es:0x1d],0x04
mov al,[0x7c00+number+0x02]
add al,0x30
mov [es:0x1e],al
mov byte [es:0x1f],0x04
mov al,[0x7c00+number+0x01]
add al,0x30
mov [es:0x20],al
mov byte [es:0x21],0x04
mov al,[0x7c00+number+0x00]
add al,0x30
mov [es:0x22],al
mov byte [es:0x23],0x04
mov byte [es:0x24],‘D’
mov byte [es:0x25],0x07
infi: jmp near infi ;无限循环
number db 0,0,0,0,0
times 203 db 0
db 0x55,0xaa
c06_mbr.asm
;代码清单6-1
;文件名:c06_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-4-12 22:12
jmp near start
mytext db ‘L’,0x07,‘a’,0x07,‘b’,0x07,‘e’,0x07,‘l’,0x07,‘ ’,0x07,‘o’,0x07,
‘f’,0x07,‘f’,0x07,‘s’,0x07,‘e’,0x07,‘t’,0x07,‘:’,0x07
number db 0,0,0,0,0
start:
mov ax,0x07c0 ;设置数据段基地址
mov ds,ax ;ds寄存器一般保存数据段基地址
mov ax,0xb800 ;设置附加段基地址
mov es,ax ;这里附加段指向显存位置,存放在es寄存器中
cld ;将方向标志位DF清零,以指示传送是负方向的,与此相对应的指令是std
mov si,mytext ;movsw指令原始数据串需要存放在ds:si位置,目的地址为es:di,因为ds目前指示的是当前代码段基地址地址,因此只要把偏移mytext存入si寄存器即可
mov di,0 ;当前es指示显存起始位置,因此只要把偏移0存入di即可
mov cx,(number-mytext)/2 ;实际上等于 13,cx作为计数器,每进行一次rep指令cx-1
rep movsw ;一次传送一个字(两个字节)
;得到标号所代表的偏移地址
mov ax,number ;此代码目的旨在显示number的偏移地址
;计算各个数位
mov bx,ax ;bx指向当前number偏移地址
mov cx,5 ;循环次数
mov si,10 ;除数
digit:
xor dx,dx ;dx(被除数高16位)清零
div si ;除法
mov [bx],dl ;保存数位 ;为什么这里不用加0x7c00了?
inc bx ;bx自加1,指向下一个内存单元,number一共定义了5个字节内存单元
loop digit
;显示各个数位
mov bx,number
mov si,4
show:
mov al,[bx+si] ;从后往前显示
add al,0x30
mov ah,0x04
mov [es:di],ax
add di,2
dec si
jns show ;上一条指令符号位为SF=0(结果为非负)时跳转
mov word [es:di],0x0744
jmp near $ ;无限循环
times 510-($-$$) db 0 ;512字节减去之后两个db指令=510字节,$当前指令偏移,$$当前代码段起始位置,填充字节0(db 0)
db 0x55,0xaa
c07_mbr.asm
;代码清单7-1
;文件名:c07_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-4-13 18:02
jmp near start
message db ‘1+2+3+.。.+100=’
start:
mov ax,0x7c0 ;设置数据段的段基地址
mov ds,ax
mov ax,0xb800 ;设置附加段基址到显示缓冲区
mov es,ax
;以下显示字符串
mov si,message
mov di,0
mov cx,start-message
@g:
mov al,[si] ;因为这是硬盘主引导扇区代码,因此被加载到0x7c00,[si]=[ds:si],就是相对于代码段开头的相对偏移,这个相对偏移就是标签message的值
mov [es:di],al
inc di ;di用做显存段地址的相对偏移,字符内容信息放在低一个字节
mov byte [es:di],0x07
inc di ;字符显示信息放在高一个字节
inc si ;si用作寻址字符串相对偏移
loop @g
;以下计算1到100的和
xor ax,ax ;清空ax寄存器,存放结果
mov cx,1
@f:
add ax,cx
inc cx ;cx做累加器
cmp cx,100
jle @f ;小于等于时跳转
;以下计算累加和的每个数位
xor cx,cx ;设置堆栈段的段基地址
mov ss,cx
mov sp,cx ;堆栈段指针和段基址都在0x0000处,堆栈段从高地址向低地址生长
mov bx,10
xor cx,cx
@d:
inc cx ;压栈中用cx记录一共压入栈元素个数,以便之后出栈时能及时停止pop
xor dx,dx ;被除数[dx:ax]
div bx ;除数bx
or dl,0x30 ;余数在dx中,但是余数最多到9,因此在dl中就够了,加0x30得到ASCII码
push dx ;dx中只有dl有意义,但是压栈的单位必须是字(两个字节)
cmp ax,0
jne @d ;循环跳出时,结果5050每一位被放在栈中
;以下显示各个数位
@a:
pop dx ;出栈,栈顶元素是千位,百位,十位,个位
mov [es:di],dl
inc di
mov byte [es:di],0x07
inc di
loop @a
jmp near $
8086寻址方式总结:
1.寄存器寻址 :mov ax,cx
2.立即寻址 :add bx,0xf000 (目的操作数立即数寻址,源操作数寄存器寻址)
3.直接寻址 :mov ax,[0x5c0f]
4.基址寻址:利用基址寄存器bx/bp中的值作为偏移地址,其中bx默认段寄存器为ds(数据段),bp默认段寄存器为ss(堆栈段)。堆栈段指针寄存器为sp,不要混淆。
如:mov dx,[bp-2]
5.变址寻址:利用变址寄存器si/di中的值作为偏移地址,默认段寄存器为ds
如:mov [si+0x100],al
6.基址变址寻址:利用基址寄存器bx/bp和变址寄存器si/di相加作为偏移地址
如:add word [bx+di],0x3000
加载程序 源程序c08_mbr.asm
;代码清单8-1
;文件名:c08_mbr.asm
;文件说明:硬盘主引导扇区代码(加载程序)
;创建日期:2011-5-5 18:17
app_lba_start equ 100 ;声明常数(用户程序起始逻辑扇区号)
;常数的声明不会占用汇编地址
SECTION mbr align=16 vstart=0x7c00
;设置堆栈段和栈指针
mov ax,0
mov ss,ax
mov sp,ax
mov ax,[cs:phy_base] ;计算用于加载用户程序的逻辑段地址
mov dx,[cs:phy_base+0x02]
mov bx,16
div bx
mov ds,ax ;令DS和ES指向该段以进行操作
mov es,ax
;以下读取程序的起始部分
xor di,di
mov si,app_lba_start ;程序在硬盘上的起始逻辑扇区号
xor bx,bx ;加载到DS:0x0000处
call read_hard_disk_0
;以下判断整个程序有多大
mov dx,[2] ;曾经把dx写成了ds,花了二十分钟排错
mov ax,[0]
mov bx,512 ;512字节每扇区
div bx
cmp dx,0
jnz @1 ;未除尽,因此结果比实际扇区数少1
dec ax ;已经读了一个扇区,扇区总数减1
@1:
cmp ax,0 ;考虑实际长度小于等于512个字节的情况
jz direct
;读取剩余的扇区
push ds ;以下要用到并改变DS寄存器
mov cx,ax ;循环次数(剩余扇区数)
@2:
mov ax,ds
add ax,0x20 ;得到下一个以512字节为边界的段地址
mov ds,ax
xor bx,bx ;每次读时,偏移地址始终为0x0000
inc si ;下一个逻辑扇区
call read_hard_disk_0
loop @2 ;循环读,直到读完整个功能程序
pop ds ;恢复数据段基址到用户程序头部段
;计算入口点代码段基址
direct:
mov dx,[0x08]
mov ax,[0x06]
call calc_segment_base
mov [0x06],ax ;回填修正后的入口点代码段基址
;开始处理段重定位表
mov cx,[0x0a] ;需要重定位的项目数量
mov bx,0x0c ;重定位表首地址
realloc:
mov dx,[bx+0x02] ;32位地址的高16位
mov ax,[bx]
call calc_segment_base
mov [bx],ax ;回填段的基址
add bx,4 ;下一个重定位项(每项占4个字节)
loop realloc
jmp far [0x04] ;转移到用户程序
;-------------------------------------------------------------------------------
read_hard_disk_0: ;从硬盘读取一个逻辑扇区
;输入:DI:SI=起始逻辑扇区号
; DS:BX=目标缓冲区地址
push ax
push bx
push cx
push dx
mov dx,0x1f2
mov al,1
out dx,al ;读取的扇区数
inc dx ;0x1f3
mov ax,si
out dx,al ;LBA地址7~0
inc dx ;0x1f4
mov al,ah
out dx,al ;LBA地址15~8
inc dx ;0x1f5
mov ax,di
out dx,al ;LBA地址23~16
inc dx ;0x1f6
mov al,0xe0 ;LBA28模式,主盘
or al,ah ;LBA地址27~24
out dx,al
inc dx ;0x1f7
mov al,0x20 ;读命令
out dx,al
.waits:
in al,dx
and al,0x88
cmp al,0x08
jnz .waits ;不忙,且硬盘已准备好数据传输
mov cx,256 ;总共要读取的字数
mov dx,0x1f0
.readw:
in ax,dx
mov [bx],ax
add bx,2
loop .readw
pop dx
pop cx
pop bx
pop ax
ret
;-------------------------------------------------------------------------------
calc_segment_base: ;计算16位段地址
;输入:DX:AX=32位物理地址
;返回:AX=16位段基地址
push dx
add ax,[cs:phy_base]
adc dx,[cs:phy_base+0x02]
shr ax,4
ror dx,4
and dx,0xf000
or ax,dx
pop dx
ret
;-------------------------------------------------------------------------------
phy_base dd 0x10000 ;用户程序被加载的物理起始地址
times 510-($-$$) db 0
db 0x55,0xaa
举报