c汇编语言程序框架培训,[010][x86汇编语言]学习用户程序的编写(c08.asm)
源程序來源
加載程序
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寄存器。
總結
以上是生活随笔為你收集整理的c汇编语言程序框架培训,[010][x86汇编语言]学习用户程序的编写(c08.asm)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于anaconda下载包的位置的问题
- 下一篇: spring boot 集成 sitem