源程序来源
加载程序
c08_mbr.asm
用户源程序:增加注释
;
;文件名:c08-2.asm
;文件说明:用户程序
;创建日期:13:08 2018/5/23
;----------------------------------------------------------------------
SECTION header vstart=0 ;定义用户程序头部段
program_length dd program_end ;程序总长度[0x00]
;用户程序入口点
code_entry dw start ;偏移地址[0x04] 此处的start来源于自己的命名
dd section.code_1.start ;段地址[0x06] 此处.start是汇编指令的语法
realloc_tbl_len dw (header_end - code_1_segment)/4
;段重定位表项个数[0x0a]
;1个表项占4个字节
;段重定位表项
code_1_segment dd section.code_1.start ;[0x0c]
code_2_segment dd section.code_2.start ;[0x10]
data_1_segment dd section.data_1.start ;[0x14]
data_2_segment dd section.data_2.start ;[0x18]
stack_segment dd section.stack.start ;[0x1c]
header_end:
;----------------------------------------------------------------------
SECTION code_1 align=16 vstart=0 ;定义代码段1(16字节对齐)
put_string: ;显示串(字符串以0结尾)
mov cl,[bx] ;取一个字符
or cl,cl ;cl=0?
jz .exit ;cl=0时返回主程序
call put_char
inc bx ;下一个字符
jmp put_string
.exit:
ret
;----------------------------------------------------------------------
put_char: ;显示一个字符
push ax
push bx
push cx
push dx
push ds
push es
;以下取当前光标:光标位置是一个16位的数值
mov dx,0x3d4 ;索引寄存器端口号 0x3d4
mov al,0x0e ;索引值14
out dx,al
mov dx,0x3d5 ;数据端口0x3d5
in al,dx ;高8位
mov ah,al
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
in al,dx ;低8位
mov bx,ax ;BX 存放代表光标位置的16位数
cmp cl,0x0d ;回车符?
jnz .put_0a ;不是回车,看看是不是换行等字符?
mov ax,bx
mov bl,80
div bl
mul bl
mov bx,ax
jmp .set_cursor
.put_0a:
cmp cl,0x0a ;换行符?
jnz .put_other ;不是换行符,则正常显示字符
add bx,80
jmp .roll_screen
.put_other:
mov ax,0xb800
mov es,ax
shl bx,1 ;左移1位相当于乘以2
mov [es:bx],cl ;于光标处显示字符
;以下将光标位置推进一个字符
shr bx,1 ;右移相当于除以2,换回来bx
add bx,1 ;推进光标位置
.roll_screen: ;VGA文本模式,每行80个字符,25行
cmp bx,2000 ;光标超出屏幕?滚屏
jl .set_cursor
mov ax,0xb800
mov ds,ax
mov es,ax
cld
mov si,0xa0 ;从屏幕第2行第0列开始向上复制一行
mov di,0x00
mov cx,1920 ;80*25-80=1920
rep movsw ;以字为单位进行复制
mov bx,3840 ;4000 - 160 = 3840 清楚屏幕最底一行
mov cx,80 ;
.cls:
mov word [es:bx],0x0720 ;黑底白字的空格
add bx,2
loop .cls
mov bx,1920 ;光标位置是最后一行行首
;设置光标
.set_cursor: ;不同的情况已用不同的方法将光标的新位置计算好
mov dx,0x3d4
mov al,0x0e ;高8位
out dx,al
mov dx,0x3d5
mov al,bh
out dx,al
mov dx,0x3d4
mov al,0x0f ;低8位
out dx,al
mov dx,0x3d5
mov al,bl
out dx,al
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
ret
;----------------------------------------------------------------------
start:
;初始化执行时,DS和ES指向用户程序头部段
mov ax,[stack_segment] ;设置到用户程序到自己的堆栈
mov ss,ax
mov sp,stack_end ;stack段里保留保留256字节的空间 mov sp,256
mov ax,[data_1_segment] ;设置到用户程序自己的数据段
mov ds,ax
mov bx,msg0 ;显示第一段信息
call put_string ;put_string用[bx]取每一个字符
push word [es:code_2_segment] ;段寄存器DS切换到数据段2
mov ax,begin
push ax
retf ;转移到代码段2执行
continue:
mov ax,[es:data_2_segment] ;段寄存器DS切换到数据段2
mov ds,ax
mov bx,msg1
call put_string ;显示第二段信息
jmp $
;----------------------------------------------------------------------
SECTION code_2 align=16 vstart=0 ;定义代码段2(16字节对齐)
begin:
push word [es:code_1_segment]
mov ax,continue
push ax
retf ;转移到代码段1接着执行
;----------------------------------------------------------------------
SECTION data_1 align=16 vstart=0
msg0 db ' This is NASM - the famous Netwide Assembler. '
db 'Back at SourceForge and in intensive development! '
db 'Get the current versions from http://www.nasm.us/.'
db 0x0d,0x0a,0x0d,0x0a
db ' Example code for calculate 1+2+...+1000:',0x0d,0x0a,0x0d,0x0a
db ' xor dx,dx',0x0d,0x0a
db ' xor ax,ax',0x0d,0x0a
db ' xor cx,cx',0x0d,0x0a
db ' @@:',0x0d,0x0a
db ' inc cx',0x0d,0x0a
db ' add ax,cx',0x0d,0x0a
db ' adc dx,0',0x0d,0x0a
db ' inc cx',0x0d,0x0a
db ' cmp cx,1000',0x0d,0x0a
db ' jle @@',0x0d,0x0a
db ' ... ...(Some other codes)',0x0d,0x0a,0x0d,0x0a
db 0
;----------------------------------------------------------------------
SECTION data_2 align=16 vstart=0
msg1 db ' The above contents is written by LeeChung. '
db '2011-05-06'
db 0
;----------------------------------------------------------------------
SECTION stack align=16 vstart=0
resb 256
stack_end:
;----------------------------------------------------------------------
SECTION trail align=16
program_end: ;trail段没有vstart标记,program_end是针对用户程序开头的偏移量
代码说明
循环与子程序的调用关系
循环
put_string
参数
DS 段寄存器器指向数据段
BX 指向字符串的标号所在(位于数据段内,vstart=0 规定是相对数据段开头的偏移量)
-----------------------------------------------------------
-----------------------------------------------------------
子程序
put_char
功能
显示一个字符(注意是一个)
参数
cl 要显示的字符
过程
见下方put_char流程图
-----------------------------------------------------------
-----------------------------------------------------------
光标位置的计算与光标的显示分开的,由不同的情况计算出不同的位置数值,再统一由 子程序.setcursor 进行显示。
图8-19 过程put_char的流程图.png
《x86汇编语言:从实模式到保护模式》 第143页
光标的读与写
指定索引寄存器 端口号是 0x3d4
光标位置是16位数,索引值 14(0x0e)和 15(0x0f)提供光标位置的高8位、低8位
指定好寄存器之后,通过数据端口 0x3d5 进行读写
取光标位置,取到的位置16位数值放到 BX里面
===================================================
;以下取当前光标:光标位置是一个16位的数值
mov dx,0x3d4 ;索引寄存器端口号 0x3d4
mov al,0x0e ;索引值14
out dx,al
mov dx,0x3d5 ;数据端口0x3d5
in al,dx ;高8位
mov ah,al
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
in al,dx ;低8位
mov bx,ax ;BX 存放代表光标位置的16位数
---------------------------------------------------------------------------------------------
写光标位置,根据不同情况计算好的光标位置通过端口写
===============================================================
;设置光标
.set_cursor: ;不同的情况已用不同的方法将光标的新位置计算好
mov dx,0x3d4
mov al,0x0e ;高8位
out dx,al
mov dx,0x3d5
mov al,bh
out dx,al
mov dx,0x3d4
mov al,0x0f ;低8位
out dx,al
mov dx,0x3d5
mov al,bl
out dx,al
-------------------------------------------------------------------------------------------------------------------
显存、光标、字符
VGA 文本模式 一页有25行,每行有80个字符
指定 es = 0xb800 di= 0x0000
假设要在第0行第0列,显示一个字符‘a’
1、需要在偶数地址写字符
mov es:[di],'a'
2、在奇数地址写颜色属性,黑底白字0x07
mov es:[di+1],0x07
因此可以看到一页能显示 80*25=2000个字符,
但同时也是存了4000个字节的数据
(一半是字符的ASCII码,一半是每个字符的对应属性)
光标的位置是一个16位的数值,
因此,对光标而言,
0表示第0行0列,
1表示第0行1列,
....
20表示第0行20列,
每一行有80列,
79表示第0行79列,
80表示第1行第0行(换行...)
....
==========================================
在子程序
.put_other:
mov ax,0xb800
mov es,ax
shl bx,1 ;左移1位相当于乘以2
mov [es:bx],cl ;于光标处显示字符
;以下将光标位置推进一个字符
shr bx,1 ;右移相当于除以2,换回来bx
add bx,1 ;推进光标位置
是通过 光标的位置数值 来确定 字符的显示位置的,
光标在哪里闪烁,字符就写到那里,
【光标位置数值 x 2 = 字符要被送入的那个偶地址】
字符的颜色属性默认是0x07不需要修改。
SECTION关键词
在用户程序中使用SECTION关键词人为的分了很多段,然后把这些段集中放到头部段header,并且借由加载器的回写在执行时变成了都是可以映射到真实物理地址的段地址,需要用到哪个段就去头部段里取,这样少得可怜的段寄存器就够用了,如果全局固定es指向头部段,那么就疯狂复用ds寄存器。