[010][x86匯編語(yǔ)言]學(xué)習(xí)用戶程序的編寫(c08.asm)

源程序來(lái)源

http://www.itdecent.cn/p/72c151606908

加載程序

  • 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寄存器
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容