32位汇编语言学习笔记(34)--一个数据的柱状图显示程序



这个程序出自《Assembly Language step by step programming with linux》第11章,这是一个相对复杂一点的程序,将会用到我们前面几节学习的全部指令。

SECTION .data			; Section containing initialised data
	EOL 	equ 10		; Linux end-of-line character
	FILLCHR	equ 32		; ASCII space character
	HBARCHR	equ 95		; Use dash char if this won't display
	STRTROW	equ 2		; Row where the graph begins

; The dataset is just a table of byte-length numbers:
	Dataset	db 9,17,71,52,55,18,29,36,18,68,77,63,58,44,0

	Message db "Data current as of 1/9/2015"
	MSGLEN  equ $-Message

; This escape sequence will clear the console terminal and place the
; text cursor to the origin (1,1) on virtually all Linux consoles:
	ClrHome db 27,"[2J",27,"[01;01H"
	CLRLEN	equ $-ClrHome	; Length of term clear string
	
SECTION .bss			; Section containing uninitialized data	

	COLS	equ 81		; Line length + 1 char for EOL
	ROWS	equ 25		; Number of lines in display
	VidBuff	resb COLS*ROWS	; Buffer size adapts to ROWS & COLS

SECTION .text			; Section containing code

global 	_start			; Linker needs this to find the entry point!

; This macro clears the Linux console terminal and sets the cursor position
; to 1,1, using a single predefined escape sequence.
%macro	ClearTerminal 0
	pushad			; Save all registers
	mov eax,4		; Specify sys_write call
	mov ebx,1		; Specify File Descriptor 1: Standard Output
	mov ecx,ClrHome		; Pass offset of the error message
	mov edx,CLRLEN		; Pass the length of the message
	int 80H			; Make kernel call
	popad			; Restore all registers
%endmacro

Show:	pushad			; Save all registers
	mov eax,4		; Specify sys_write call
	mov ebx,1		; Specify File Descriptor 1: Standard Output
	mov ecx,VidBuff		; Pass offset of the buffer
	mov edx,COLS*ROWS	; Pass the length of the buffer
	int 80H			; Make kernel call
	popad			; Restore all registers
	ret			; And go home!

ClrVid:	push eax		; Save caller's registers
	push ecx
	push edi
	cld			; Clear DF; we're counting up-memory
	mov al,FILLCHR		; Put the buffer filler char in AL
	mov edi,VidBuff		; Point destination index at buffer
	mov ecx,COLS*ROWS	; Put count of chars stored into ECX
	rep stosb		; Blast chars at the buffer
; Buffer is cleared; now we need to re-insert the EOL char after each line:
	mov edi,VidBuff		; Point destination at buffer again
	dec edi			; Start EOL position count at VidBuff char 0
	mov ecx,ROWS		; Put number of rows in count register
PtEOL:	add edi,COLS		; Add column count to EDU
	mov byte [edi],EOL	; Store EOL char at end of row
	loop PtEOL		; Loop back if still more lines
	pop edi			; Restore caller's registers
	pop ecx
	pop eax
	ret			; and go home!

WrtLn:	push eax	; Save registers we change
	push ebx
	push ecx
	push edi
	cld		; Clear DF for up-memory write
	mov edi,VidBuff	; Load destination index with buffer address
	dec eax		; Adjust Y value down by 1 for address calculation
	dec ebx		; Adjust X value down by 1 for address calculation
	mov ah,COLS	; Move screen width to AH
	mul ah		; Do 8-bit multiply AL*AH to AX
	add edi,eax	; Add Y offset into vidbuff to EDI
	add edi,ebx	; Add X offset into vidbuf to EDI
	rep movsb	; Blast the string into the buffer
	pop edi		; Restore registers we changed
	pop ecx
	pop ebx
	pop eax
	ret		; and go home!

WrtHB:	push eax	; Save registers we change
	push ebx
	push ecx
	push edi
	cld		; Clear DF for up-memory write
	mov edi,VidBuff	; Put buffer address in destination register
	dec eax		; Adjust Y value down by 1 for address calculation
	dec ebx		; Adjust X value down by 1 for address calculation
	mov ah,COLS	; Move screen width to AH
	mul ah		; Do 8-bit multiply AL*AH to AX
	add edi,eax	; Add Y offset into vidbuff to EDI
	add edi,ebx	; Add X offset into vidbuf to EDI
	mov al,HBARCHR	; Put the char to use for the bar in AL
	rep stosb	; Blast the bar char into the buffer
	pop edi		; Restore registers we changed
	pop ecx
	pop ebx
	pop eax
	ret		; And go home!

Ruler:  push eax	; Save the registers we change
	push ebx
	push ecx
	push edi
	mov edi,VidBuff	; Load video address to EDI
	dec eax		; Adjust Y value down by 1 for address calculation
	dec ebx		; Adjust X value down by 1 for address calculation
	mov ah,COLS	; Move screen width to AH
	mul ah		; Do 8-bit multiply AL*AH to AX
	add edi,eax	; Add Y offset into vidbuff to EDI
	add edi,ebx	; Add X offset into vidbuf to EDI
; EDI now contains the memory address in the buffer where the ruler
; is to begin. Now we display the ruler, starting at that position:
    mov al,'1'	; Start ruler with digit '1'
DoChar: stosb	; Note that there's no REP prefix!
	add al,'1'	; Bump the character value in AL up by 1
    aaa		    ; Adjust AX to make this a BCD addition
	add al,'0'	; Make sure we have binary 3 in AL's high nybble
    loop DoChar	; Go back & do another char until ECX goes to 0
	pop edi		; Restore the registers we changed
	pop ecx
	pop ebx
	pop eax
	ret		; And go home!

;-------------------------------------------------------------------------
; MAIN PROGRAM:
	
_start:
	nop		; This no-op keeps gdb happy...

; Get the console and text display text buffer ready to go:
	ClearTerminal	; Send terminal clear string to console
	call ClrVid	; Init/clear the video buffer

; Next we display the top ruler:
	mov eax,1	; Load Y position to AL
	mov ebx,1	; Load X position to BL
	mov ecx,COLS-1	; Load ruler length to ECX
	call Ruler	; Write the ruler to the buffer

; Here we loop through the dataset and graph the data:
	mov esi,Dataset	; Put the address of the dataset in ESI
	mov ebx,1	; Start all bars at left margin (X=1)
	mov ebp,0	; Dataset element index starts at 0
.blast:	mov eax,ebp	; Add dataset number to element index
	add eax,STRTROW	; Bias row value by row # of first bar
	mov cl,byte [esi+ebp]	; Put dataset value in low byte of ECX
	cmp ecx,0	; See if we pulled a 0 from the dataset
	je .rule2	; If we pulled a 0 from the dataset, we're done
	call WrtHB	; Graph the data as a horizontal bar
	inc ebp		; Increment the dataset element index
	jmp .blast	; Go back and do another bar

; Display the bottom ruler:
.rule2:	mov eax,ebp	; Use the dataset counter to set the ruler row
	add eax,STRTROW	; Bias down by the row # of the first bar
	mov ebx,1	; Load X position to BL
	mov ecx,COLS-1	; Load ruler length to ECX
	call Ruler	; Write the ruler to the buffer

; Thow up an informative message centered on the last line
	mov esi,Message	; Load the address of the message to ESI
	mov ecx,MSGLEN	; and its length to ECX
	mov ebx,COLS	; and the screen width to EBX
	sub ebx,ecx	; Calc diff of message length and screen width
	shr ebx,1	; Divide difference by 2 for X value
	mov eax,ROWS-1	; Set message row to Line 24
	call WrtLn	; Display the centered message

; Having written all that to the buffer, send the buffer to the console:
	call Show	; Refresh the buffer to the console

Exit:	mov eax,1	; Code for Exit Syscall
	mov ebx,0	; Return a code of zero	
	int 80H		; Make kernel call

程序分析:
ClearTerminal宏,用于清屏,并且把光标移动到第一行第一列的位置。

Show函数,用于把VidBuff的内容显示到标准输出。

ClrVid函数,用于清理VidBuff缓存。
ClrVid: push eax  
 push ecx
 push edi
 cld   //清除DF标志位,控制内存地址变化方向从低到高。
 mov al,FILLCHR  //设置al为空格
 mov edi,VidBuff  //edi= VidBuff,目的缓存是VidBuff
 mov ecx,COLS*ROWS //填充字符数等于缓存的字节数。
 rep stosb  //把al的值循环填充到VidBuff中,循环次数是缓存的字节数。
 mov edi,VidBuff  //edi= VidBuff,目的缓存是VidBuff
 dec edi   //edi = VidBuff -1
 mov ecx,ROWS  //ecx=ROWS,循环次数是行数。
PtEOL: add edi,COLS  edi = edi + COLS,这样edi是每行的最后一列
 mov byte [edi],EOL //把最后一列设置为换行符
 loop PtEOL  //循环次数是ROWS。
 pop edi   
 pop ecx
 pop eax
 ret   

WrtLn函数的输入参数包括eax,ebx,ecx和esi。其中eax和ebx,分别表示Y和X坐标,ecx表示源字符串长度,esi是源字符串地址。此函数会把源字符串拷贝到以此坐标为起点的VidBuff中。
WrtLn: push eax 
 push ebx
 push ecx
 push edi
 cld  //清除DF标志位,控制内存地址变化方向从低到高。
 mov edi,VidBuff //edi= VidBuff
 dec eax  //eax=eax-1,y坐标是从1开始的,因此计算地址时要减1。
 dec ebx  //ebx=ebx-1,x坐标是从1开始的,因此计算地址时要减1。
 mov ah,COLS //ah = COLS
 mul ah  //ax=ah*al
 add edi,eax //edi = VidBuff+ COLS*(y-1)
 add edi,ebx //edi= VidBuff+ COLS*(y-1)+(x-1)
 rep movsb //内存拷贝,拷贝ecx个字节
 pop edi  
 pop ecx
 pop ebx
 pop eax
 ret  

WrtHB函数,用于画圆柱,因为屏幕显示的问题,实际画的是下划线。入参与WrtLn函数差不多,只是没有esi。
WrtHB: push eax 
 push ebx
 push ecx
 push edi
 cld  //清除DF标志位,控制内存地址变化方向从低到高。
 mov edi,VidBuff //edi= VidBuff
 dec eax  
 dec ebx  
 mov ah,COLS 
 mul ah  //ax=ah*al
 add edi,eax //edi = VidBuff+ COLS*(y-1)
 add edi,ebx //edi= VidBuff+ COLS*(y-1)+(x-1)
 mov al,HBARCHR //al= HBARCHR
 rep stosb //用al循环填充edi指向的缓存,循环次数是ecx
 pop edi  
 pop ecx
 pop ebx
 pop eax
 ret  

Ruler函数,用于画标尺,入参与WrtHB函数一样。
Ruler:  push eax 
 push ebx
 push ecx
 push edi
 mov edi,VidBuff //edi= VidBuff
 dec eax  
 dec ebx  
 mov ah,COLS 
 mul ah  //ax=ah*al
 add edi,eax //edi = VidBuff+ COLS*(y-1)
 add edi,ebx //edi= VidBuff+ COLS*(y-1)+(x-1)
    mov al,'1'     //标尺起始数字是’1’
DoChar: stosb 
 add al,'1'     //两个数字的ASCII码相加。
    aaa      //跳转ax寄存器,使得al是一个合法的BCD值。
 add al,'0'  //加上’0’,使得al是’0’到’9’之间的ASCII码。
    loop DoChar //循环ecx次
 pop edi  
 pop ecx
 pop ebx
 pop eax
 ret  

主程序
 ClearTerminal //清屏
 call ClrVid //初始化缓存

 mov eax,1 
 mov ebx,1 
 mov ecx,COLS-1 
 call Ruler //第一个标尺输入缓存

 mov esi,Dataset //esi= Dataset
 mov ebx,1 //x=1
 mov ebp,0 //ebp=0
.blast: mov eax,ebp //eax=ebp
 add eax,STRTROW //eax=eax+ STRTROW,y值
 mov cl,byte [esi+ebp] //cl=esi[ebp],传入长度
 cmp ecx,0 //比较ecx和0
 je .rule2 //如果ecx等于0,跳转到.rule2,结束循环
 call WrtHB //画圆柱
 inc ebp  //ebp=ebp+1
 jmp .blast  //跳转到.blast,继续循环

.rule2: mov eax,ebp //eax=ebp
 add eax,STRTROW // eax=eax+ STRTROW,y值
 mov ebx,1 //x=1
 mov ecx,COLS-1 //ecx= COLS-1
 call Ruler //画第二个标尺

 mov esi,Message //esi= Message
 mov ecx,MSGLEN //ecx= MSGLEN
 mov ebx,COLS //以下3行用于计算x的坐标位置
 sub ebx,ecx 
 shr ebx,1 
 mov eax,24 //y=24
 call WrtLn //写Message信息

 call Show  //把整个柱状图绘制信息打印出来

makefile文件内容:

vidbuff1: vidbuff1.o
	ld -o vidbuff1 vidbuff1.o
vidbuff1.o: vidbuff1.asm
	nasm -f elf -g -F stabs vidbuff1.asm

测试:

[root@bogon vidbuff1]# ./vidbuff1 







12345678901234567890123456789012345678901234567890123456789012345678901234567890
_________                                                                       
_________________                                                               
_______________________________________________________________________         
____________________________________________________                            
_______________________________________________________                         
__________________                                                              
_____________________________                                                   
____________________________________                                            
__________________                                                              
____________________________________________________________________            
_____________________________________________________________________________   
_______________________________________________________________                 
__________________________________________________________                      
____________________________________________                                    
12345678901234567890123456789012345678901234567890123456789012345678901234567890
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                          Data current as of 1/9/2015  

圆柱条显示不出来,显示的是下划线,就凑合着看吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值