c08_mbr.asmapp
;代碼清單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 start ;未除盡,所以結果比實際扇區數少1 dec ax ;已經讀了一個扇區,扇區總數減1 start: 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
c08.asmide
;代碼清單8-2 ;文件名:c08.asm ;文件說明:用戶程序 ;建立日期: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 ;段重定位表項個數[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字節對齊) put_string: ;顯示串(0結尾)。 ;輸入:DS:BX=串地址 mov cl,[bx] or cl,cl ;cl=0 ? jz .exit ;是的,返回主程序 call put_char inc 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,al mov dx,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 mov [es:bx],cl ;如下將光標位置推動一個字符 shr bx,1 add bx,1 .roll_screen: cmp bx,2000 ;光標超出屏幕?滾屏 jl .set_cursor mov ax,0xb800 mov ds,ax mov es,ax cld 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 ;------------------------------------------------------------------------------- start: ;初始執行時,DS和ES指向用戶程序頭部段 mov ax,[stack_segment] ;設置到用戶程序本身的堆棧 mov ss,ax mov sp,stack_end mov ax,[data_1_segment] ;設置到用戶程序本身的數據段 mov ds,ax mov bx,msg0 call put_string ;顯示第一段信息 push word [es:code_2_segment] mov ax,begin push ax ;能夠直接push begin,80386+ 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 ;能夠直接push continue,80386+ 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: