源程序來(lái)源
加載程序
c08_mbr.asm
用戶源程序:增加注釋
;
;文件名:c08-2.asm
;文件說(shuō)明:用戶程序
;創(chuàng)建日期:13:08 2018/5/23
;----------------------------------------------------------------------
SECTION header vstart=0 ;定義用戶程序頭部段
program_length dd program_end ;程序總長(zhǎng)度[0x00]
;用戶程序入口點(diǎn)
code_entry dw start ;偏移地址[0x04] 此處的start來(lái)源于自己的命名
dd section.code_1.start ;段地址[0x06] 此處.start是匯編指令的語(yǔ)法
realloc_tbl_len dw (header_end - code_1_segment)/4
;段重定位表項(xiàng)個(gè)數(shù)[0x0a]
;1個(gè)表項(xiàng)占4個(gè)字節(jié)
;段重定位表項(xiàng)
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字節(jié)對(duì)齊)
put_string: ;顯示串(字符串以0結(jié)尾)
mov cl,[bx] ;取一個(gè)字符
or cl,cl ;cl=0?
jz .exit ;cl=0時(shí)返回主程序
call put_char
inc bx ;下一個(gè)字符
jmp put_string
.exit:
ret
;----------------------------------------------------------------------
put_char: ;顯示一個(gè)字符
push ax
push bx
push cx
push dx
push ds
push es
;以下取當(dāng)前光標(biāo):光標(biāo)位置是一個(gè)16位的數(shù)值
mov dx,0x3d4 ;索引寄存器端口號(hào) 0x3d4
mov al,0x0e ;索引值14
out dx,al
mov dx,0x3d5 ;數(shù)據(jù)端口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 存放代表光標(biāo)位置的16位數(shù)
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位相當(dāng)于乘以2
mov [es:bx],cl ;于光標(biāo)處顯示字符
;以下將光標(biāo)位置推進(jìn)一個(gè)字符
shr bx,1 ;右移相當(dāng)于除以2,換回來(lái)bx
add bx,1 ;推進(jìn)光標(biāo)位置
.roll_screen: ;VGA文本模式,每行80個(gè)字符,25行
cmp bx,2000 ;光標(biāo)超出屏幕?滾屏
jl .set_cursor
mov ax,0xb800
mov ds,ax
mov es,ax
cld
mov si,0xa0 ;從屏幕第2行第0列開(kāi)始向上復(fù)制一行
mov di,0x00
mov cx,1920 ;80*25-80=1920
rep movsw ;以字為單位進(jìn)行復(fù)制
mov bx,3840 ;4000 - 160 = 3840 清楚屏幕最底一行
mov cx,80 ;
.cls:
mov word [es:bx],0x0720 ;黑底白字的空格
add bx,2
loop .cls
mov bx,1920 ;光標(biāo)位置是最后一行行首
;設(shè)置光標(biāo)
.set_cursor: ;不同的情況已用不同的方法將光標(biāo)的新位置計(jì)算好
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:
;初始化執(zhí)行時(shí),DS和ES指向用戶程序頭部段
mov ax,[stack_segment] ;設(shè)置到用戶程序到自己的堆棧
mov ss,ax
mov sp,stack_end ;stack段里保留保留256字節(jié)的空間 mov sp,256
mov ax,[data_1_segment] ;設(shè)置到用戶程序自己的數(shù)據(jù)段
mov ds,ax
mov bx,msg0 ;顯示第一段信息
call put_string ;put_string用[bx]取每一個(gè)字符
push word [es:code_2_segment] ;段寄存器DS切換到數(shù)據(jù)段2
mov ax,begin
push ax
retf ;轉(zhuǎn)移到代碼段2執(zhí)行
continue:
mov ax,[es:data_2_segment] ;段寄存器DS切換到數(shù)據(jù)段2
mov ds,ax
mov bx,msg1
call put_string ;顯示第二段信息
jmp $
;----------------------------------------------------------------------
SECTION code_2 align=16 vstart=0 ;定義代碼段2(16字節(jié)對(duì)齊)
begin:
push word [es:code_1_segment]
mov ax,continue
push ax
retf ;轉(zhuǎn)移到代碼段1接著執(zhí)行
;----------------------------------------------------------------------
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段沒(méi)有vstart標(biāo)記,program_end是針對(duì)用戶程序開(kāi)頭的偏移量
代碼說(shuō)明
- 循環(huán)與子程序的調(diào)用關(guān)系
循環(huán)
put_string
參數(shù)
DS 段寄存器器指向數(shù)據(jù)段
BX 指向字符串的標(biāo)號(hào)所在(位于數(shù)據(jù)段內(nèi),vstart=0 規(guī)定是相對(duì)數(shù)據(jù)段開(kāi)頭的偏移量)
-----------------------------------------------------------
-----------------------------------------------------------
子程序
put_char
功能
顯示一個(gè)字符(注意是一個(gè))
參數(shù)
cl 要顯示的字符
過(guò)程
見(jiàn)下方put_char流程圖
-----------------------------------------------------------
-----------------------------------------------------------
光標(biāo)位置的計(jì)算與光標(biāo)的顯示分開(kāi)的,由不同的情況計(jì)算出不同的位置數(shù)值,再統(tǒng)一由 子程序.setcursor 進(jìn)行顯示。
圖8-19 過(guò)程put_char的流程圖.png
《x86匯編語(yǔ)言:從實(shí)模式到保護(hù)模式》 第143頁(yè)
- 光標(biāo)的讀與寫
指定索引寄存器 端口號(hào)是 0x3d4
光標(biāo)位置是16位數(shù),索引值 14(0x0e)和 15(0x0f)提供光標(biāo)位置的高8位、低8位
指定好寄存器之后,通過(guò)數(shù)據(jù)端口 0x3d5 進(jìn)行讀寫
取光標(biāo)位置,取到的位置16位數(shù)值放到 BX里面
===================================================
;以下取當(dāng)前光標(biāo):光標(biāo)位置是一個(gè)16位的數(shù)值
mov dx,0x3d4 ;索引寄存器端口號(hào) 0x3d4
mov al,0x0e ;索引值14
out dx,al
mov dx,0x3d5 ;數(shù)據(jù)端口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 存放代表光標(biāo)位置的16位數(shù)
---------------------------------------------------------------------------------------------
寫光標(biāo)位置,根據(jù)不同情況計(jì)算好的光標(biāo)位置通過(guò)端口寫
===============================================================
;設(shè)置光標(biāo)
.set_cursor: ;不同的情況已用不同的方法將光標(biāo)的新位置計(jì)算好
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
-------------------------------------------------------------------------------------------------------------------
- 顯存、光標(biāo)、字符
VGA 文本模式 一頁(yè)有25行,每行有80個(gè)字符
指定 es = 0xb800 di= 0x0000
假設(shè)要在第0行第0列,顯示一個(gè)字符‘a(chǎn)’
1、需要在偶數(shù)地址寫字符
mov es:[di],'a'
2、在奇數(shù)地址寫顏色屬性,黑底白字0x07
mov es:[di+1],0x07
因此可以看到一頁(yè)能顯示 80*25=2000個(gè)字符,
但同時(shí)也是存了4000個(gè)字節(jié)的數(shù)據(jù)
(一半是字符的ASCII碼,一半是每個(gè)字符的對(duì)應(yīng)屬性)
光標(biāo)的位置是一個(gè)16位的數(shù)值,
因此,對(duì)光標(biāo)而言,
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位相當(dāng)于乘以2
mov [es:bx],cl ;于光標(biāo)處顯示字符
;以下將光標(biāo)位置推進(jìn)一個(gè)字符
shr bx,1 ;右移相當(dāng)于除以2,換回來(lái)bx
add bx,1 ;推進(jìn)光標(biāo)位置
是通過(guò) 光標(biāo)的位置數(shù)值 來(lái)確定 字符的顯示位置的,
光標(biāo)在哪里閃爍,字符就寫到那里,
【光標(biāo)位置數(shù)值 x 2 = 字符要被送入的那個(gè)偶地址】
字符的顏色屬性默認(rèn)是0x07不需要修改。
-
SECTION關(guān)鍵詞
在用戶程序中使用SECTION關(guān)鍵詞人為的分了很多段,然后把這些段集中放到頭部段header,并且借由加載器的回寫在執(zhí)行時(shí)變成了都是可以映射到真實(shí)物理地址的段地址,需要用到哪個(gè)段就去頭部段里取,這樣少得可憐的段寄存器就夠用了,如果全局固定es指向頭部段,那么就瘋狂復(fù)用ds寄存器。
