上一篇博文我們用了很大的篇幅說了加載器,這一篇我們該說說用戶程序了。 
 
         ;代碼清單
8 -
2 ;文件名:c08.asm;文件說明:用戶程序 ;創(chuàng)建日期:
2011 -
5 -
5  18 :
17 ;===============================================================================
SECTION header vstart=
0                      ;定義用戶程序頭部段 program_length  dd program_end          ;程序總長度[
0x00 ];用戶程序入口點code_entry      dw start                ;偏移地址[
0x04 ]dd section.code_1.start ;段地址[
0x06 ] realloc_tbl_len dw (header_end-code_1_segment)/
4 ;段重定位表項個數(shù)[
0x0a ];段重定位表           code_1_segment  dd section.code_1.start ;[
0x0c ]code_2_segment  dd section.code_2.start ;[
0x10 ]data_1_segment  dd section.data_1.start ;[
0x14 ]data_2_segment  dd section.data_2.start ;[
0x18 ]stack_segment   dd section.stack.start  ;[
0x1c ]header_end:                ;===============================================================================
SECTION code_1 align=
16  vstart=
0          ;定義代碼段
1 (
16 字節(jié)對齊) 
put_string:                              ;顯示串(
0 結(jié)尾)。;輸入:DS:BX=串地址mov cl,[bx]
or  cl,cl                        ;cl=
0  ?jz .
exit                         ;是的,返回主程序 call put_charinc bx                          ;下一個字符 jmp put_string.
exit :ret;-------------------------------------------------------------------------------
put_char:                                ;顯示一個字符;輸入:cl=字符ascii
push  ax
push  bx
push  cx
push  dx
push  ds
push  es;以下取當前光標位置mov dx,
0x3d4 mov al,
0x0e out dx,almov dx,
0x3d5 in al,dx                        ;高
8 位 mov ah,almov dx,
0x3d4 mov al,
0x0f out dx,almov dx,
0x3d5 in al,dx                        ;低
8 位 mov bx,ax                       ;BX=代表光標位置的
16 位數(shù)cmp cl,
0x0d                      ;回車符?jnz .put_0a                     ;不是。看看是不是換行等字符 mov ax,bx                       ;此句略顯多余,但去掉后還得改書,麻煩 mov bl,
80                        div blmul blmov bx,axjmp .set_cursor.put_0a:cmp cl,
0x0a                      ;換行符?jnz .put_other                  ;不是,那就正常顯示字符 add bx,
80 jmp .roll_screen.put_other:                             ;正常顯示字符mov ax,
0xb800 mov es,axshl bx,
1 mov [es:bx],cl;以下將光標位置推進一個字符shr bx,
1 add bx,
1 .roll_screen:cmp bx,
2000                      ;光標超出屏幕?滾屏jl .set_cursormov ax,
0xb800 mov ds,axmov es,axcldmov si,
0xa0 mov di,
0x00 mov cx,
1920 rep movswmov bx,
3840                      ;清除屏幕最底一行mov cx,
80 .cls:mov word[es:bx],
0x0720           ; spaceadd bx,
2 loop .clsmov bx,
1920 .set_cursor:mov dx,
0x3d4 mov al,
0x0e out dx,almov dx,
0x3d5 mov al,bhout dx,almov dx,
0x3d4 mov al,
0x0f out dx,almov dx,
0x3d5 mov al,blout dx,al
pop  es
pop  ds
pop  dx
pop  cx
pop  bx
pop  axret;----------------------------------  用戶程序入口 --------------------------------------------start:;初始執(zhí)行時,DS和ES指向用戶程序頭部段mov ax,[stack_segment]           ;設置到用戶程序自己的堆棧 mov ss,axmov sp,stack_endmov ax,[data_1_segment]          ;設置到用戶程序自己的數(shù)據(jù)段mov ds,axmov bx,msg
0 call put_string                  ;顯示第一段信息 
push  word [es:code_2_segment]mov ax,begin
push  ax                          ;可以直接
push  begin,
80386 +retf                             ;轉(zhuǎn)移到代碼段
2 執(zhí)行 
continue :mov ax,[es:data_2_segment]       ;段寄存器DS切換到數(shù)據(jù)段
2  mov ds,axmov bx,msg1call put_string                  ;顯示第二段信息 jmp $ ;===============================================================================
SECTION code_2 align=
16  vstart=
0           ;定義代碼段
2 (
16 字節(jié)對齊)begin:
push  word [es:code_1_segment]mov ax,
continue push  ax                          ;可以直接
push  continue ,
80386 +retf                             ;轉(zhuǎn)移到代碼段
1 接著執(zhí)行 ;===============================================================================
SECTION data_1 align=
16  vstart=
0 msg
0  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:
 
接下來我們分塊分析。
 
SECTION header vstart=0                     ;定義用戶程序頭部段 program_length  dd program_end          ;程序總長度[0x00];用戶程序入口點code_entry      dw 
start                 ;偏移地址[0x04]dd section.code_1.
start  ;段地址[0x06] realloc_tbl_len dw (header_end-code_1_segment)/4;段重定位表項個數(shù)[0x0a];段重定位表           code_1_segment  dd section.code_1.
start  ;[0x0c]code_2_segment  dd section.code_2.
start  ;[0x10]data_1_segment  dd section.data_1.
start  ;[0x14]data_2_segment  dd section.data_2.
start  ;[0x18]stack_segment   dd section.stack.
start   ;[0x1c]header_end:                 
這段代碼用來定義用戶程序的頭部。頭部格式在上一篇博文已經(jīng)說過了。 
 
如圖所示: 
 
因為程序的入口點在code_1段中,所以是
 
 dw 
start                   ;偏移地址[0x04]dd section.code_1.
start    ;段地址[0x06]  
其他語句源碼中都有注釋,很好理解。
 
put_char:                                 push  ax
push  bx
push  cx
push  dx
push  ds
push  es
mov  dx,
0x3d4 mov  al,
0x0e out  dx,al
mov  dx,
0x3d5 in  al,dx                        
mov  ah,al
mov  dx,
0x3d4 mov  al,
0x0f out  dx,al
mov  dx,
0x3d5 in  al,dx                        
mov  bx,ax                       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,axshl bx,
1 mov  [es:bx],cl
mov  [es:bx+
1 ],ch shr bx,
1 add  bx,
1 .roll _screen:cmp bx,
2000                      jl 
.set _cursor
mov  ax,
0xb800 mov  ds,ax
mov  es,axcld
mov  si,
0xa0 mov  di,
0x00 mov  cx,
1920 rep movsw
mov  bx,
3840                      mov  cx,
80 .cls :
mov  word[es:bx],
0x0720           add  bx,
2 loop 
.cls mov  bx,
1920 .set _cursor:
mov  dx,
0x3d4 mov  al,
0x0e out  dx,al
mov  dx,
0x3d5 mov  al,bh
out  dx,al
mov  dx,
0x3d4 mov  al,
0x0f out  dx,al
mov  dx,
0x3d5 mov  al,bl
out  dx,al
pop  es
pop  ds
pop  dx
pop  cx
pop  bx
pop  ax
ret 
 
以上這段代碼是為了在光標位置處顯示一個字符,并推進光標到下一個字符,還考慮到了滾屏。這段代碼書中有詳細的講解,這里就不贅述了。 
 
put_string:                               mov  cl,[bx]
or  cl,cl                        jz 
.exit                         call  put_char
inc  bx                          
jmp  put_string
.exit :
ret  
這段代碼又是一個過程,里面調(diào)用了put_char,看來過程是可以嵌套的! 
 
我們從程序入口處看,要強調(diào)的是,當加載器把執(zhí)行權(quán)交給用戶程序的時候,DS和ES都指向用戶程序的頭部段,也就是指向用戶程序的最開始。start:
mov  ax,[stack_segment]           
mov  ss,ax
mov  sp,stack_end
 
mov ax,[stack_segment] ;這句指令是到段的重定位表中取修正后的SS段的段基址。
 
         mov  ax,[data_1_segment]          
mov  ds,ax
mov  bx,msg0
call  put_string                   
從重定位表中獲得重定位之后data_1段的基址,賦值給ds,這樣之后,ds就不再指向用戶程序的頭部,而是指向data_1段。
 
         push word [es:code_2_segment]mov ax,
begin push ax               ;可以直接push 
begin ,80386 +retf                  ;轉(zhuǎn)移到代碼段2執(zhí)行  
這段代碼用段超越前綴 es:code_2_segment 訪問重定位表,把code_2段的基地址壓棧。(因為此時ds已經(jīng)不再指向用戶頭部了,但是es還是指向用戶頭部);然后再把code_2段內(nèi)的一個標號begin(代表偏移地址)也壓棧。 
 
;======================================================
SECTION code_2 align=
16  vstart=
0           ;定義代碼段
2 (
16 字節(jié)對齊)begin:
push  word [es:code_1_segment]mov ax,
continue push  ax                          ;可以直接
push  continue ,
80386 +retf                             ;轉(zhuǎn)移到代碼段
1 接著執(zhí)行  
代碼段2其實什么也沒有干,干的事情就是轉(zhuǎn)移到代碼段1的continue處,原理和上面一樣。 
 
continue: mov  ax,[es:data_2_segment]       
mov  ds,ax
mov  bx,msg1
call  put_string                  
jmp  $  
這段代碼就是調(diào)用過程,顯示信息。
 
好了,下面我們可以把代碼修改一下,顯示自己想要的東西。 
 
我們也可以自定義要顯示的字符。 
 
         ;代碼清單
8 -
2 ;文件名:c08.asm;文件說明:用戶程序 ;創(chuàng)建日期:
2011 -
5 -
5  18 :
17 ;===============================================================================
SECTION header vstart=
0                      ;定義用戶程序頭部段 program_length  dd program_end          ;程序總長度[
0x00 ];用戶程序入口點code_entry      dw start                ;偏移地址[
0x04 ]dd section.code_1.start ;段地址[
0x06 ] realloc_tbl_len dw (header_end-code_1_segment)/
4 ;段重定位表項個數(shù)[
0x0a ];段重定位表           code_1_segment  dd section.code_1.start ;[
0x0c ]code_2_segment  dd section.code_2.start ;[
0x10 ]data_1_segment  dd section.data_1.start ;[
0x14 ]data_2_segment  dd section.data_2.start ;[
0x18 ]stack_segment   dd section.stack.start  ;[
0x1c ]header_end:                ;===============================================================================
SECTION code_1 align=
16  vstart=
0          ;定義代碼段
1 (
16 字節(jié)對齊) 
put_string:                              ;顯示串(
0 結(jié)尾)。;輸入:DS:BX=串地址;ch:屬性mov cl,[bx]                     
or  cl,cl                        ;cl=
0  ?jz .
exit                         ;是的,返回主程序 call put_charinc bx                          ;下一個字符 jmp put_string.
exit :ret;-------------------------------------------------------------------------------
put_char:                                ;顯示一個字符;輸入:cl=字符ascii
push  ax
push  bx
push  cx
push  dx
push  ds
push  es;以下取當前光標位置mov dx,
0x3d4 mov al,
0x0e out dx,almov dx,
0x3d5 in al,dx                        ;高
8 位 mov ah,almov dx,
0x3d4 mov al,
0x0f out dx,almov dx,
0x3d5 in al,dx                        ;低
8 位 mov bx,ax                       ;BX=代表光標位置的
16 位數(shù)cmp cl,
0x0d                      ;回車符?jnz .put_0a                     ;不是。看看是不是換行等字符 mov ax,bx                       ;此句略顯多余,但去掉后還得改書,麻煩 mov bl,
80                        div blmul blmov bx,axjmp .set_cursor.put_0a:cmp cl,
0x0a                      ;換行符?jnz .put_other                  ;不是,那就正常顯示字符 add bx,
80 jmp .roll_screen.put_other:                             ;正常顯示字符mov ax,
0xb800 mov es,axshl bx,
1 mov [es:bx],clmov [es:bx+
1 ],ch;以下將光標位置推進一個字符shr bx,
1 add bx,
1 .roll_screen:cmp bx,
2000                      ;光標超出屏幕?滾屏jl .set_cursormov ax,
0xb800 mov ds,axmov es,axcldmov si,
0xa0 mov di,
0x00 mov cx,
1920 rep movswmov bx,
3840                      ;清除屏幕最底一行mov cx,
80 .cls:mov word[es:bx],
0x0720           ; spaceadd bx,
2 loop .clsmov bx,
1920 .set_cursor:mov dx,
0x3d4 mov al,
0x0e out dx,almov dx,
0x3d5 mov al,bhout dx,almov dx,
0x3d4 mov al,
0x0f out dx,almov dx,
0x3d5 mov al,blout dx,al
pop  es
pop  ds
pop  dx
pop  cx
pop  bx
pop  axret;----------------------------------  用戶程序入口 --------------------------------------------start:;初始執(zhí)行時,DS和ES指向用戶程序頭部段mov ax,[stack_segment]           ;設置到用戶程序自己的堆棧 mov ss,axmov sp,stack_endmov ax,[data_1_segment]          ;設置到用戶程序自己的數(shù)據(jù)段mov ds,axmov bx,msg
0 mov ch,
0x02  ;greencall put_string                  ;顯示第一段信息 
push  word [es:code_2_segment]mov ax,begin
push  ax                          ;可以直接
push  begin,
80386 +retf                             ;轉(zhuǎn)移到代碼段
2 執(zhí)行 
continue :mov ax,[es:data_2_segment]       ;段寄存器DS切換到數(shù)據(jù)段
2  mov ds,axmov bx,msg1mov ch,
0x04   ;redcall put_string                  ;顯示第二段信息 
;這里我們顯示出多彩的Hellomov cx,
128      ;循環(huán)次數(shù)mov ah,
0 
@1 :
push  cxmov bx,msg2mov ch,ah   call put_string                  ;顯示Hello
pop  cxinc ah    ;屬性值增加
1 loop 
@1 jmp $ ;===============================================================================
SECTION code_2 align=
16  vstart=
0           ;定義代碼段
2 (
16 字節(jié)對齊)begin:
push  word [es:code_1_segment]mov ax,
continue push  ax                          ;可以直接
push  continue ,
80386 +retf                             ;轉(zhuǎn)移到代碼段
1 接著執(zhí)行 ;===============================================================================
SECTION data_1 align=
16  vstart=
0 msg
0  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 
0x0d ,
0x0a ,
0x0d ,
0x0a db 
0 msg2 db 
'Hello' db 
0 
;===============================================================================
SECTION stack align=
16  vstart=
0 times  256  db 
0 stack_end:  ;===============================================================================
SECTION trail align=
16 program_end:
 
OK,看一下結(jié)果吧,這就是多彩的Hello 
 
【the end 】
                            總結(jié) 
                            
                                以上是生活随笔 為你收集整理的硬盘和显卡的访问与控制(三)(含多彩的Hello)——《x86汇编语言:从实模式到保护模式》读书笔记03 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                            
                                如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。