这是《Assembly Language step by step programming with linux》书中的最后一个程序,也是全书中的最复杂的一个程序。
首先看一下这个程序使用的一些新的c接口:
FILE *fopen( const char *filename, const char *mode );
int fclose( FILE *stream );
char *fgets( char *string, int n, FILE *stream );
int fprintf( FILE *stream, const char *format [, argument ]...);
int sscanf( const char *buffer, const char *format [, argument ] ... );

此程序分成两个文件:linlib.asm和textfile.asm。linlib.asm实现几个公共函数,主程序在textfile.asm中。
linlib.asm文件内容如下:

[SECTION .data]      ; Section containing initialised data[SECTION .bss]     ; Section containing uninitialized data[SECTION .text]      ; Section containing codeextern printf      ; All of these are in the standard C library glibc
extern rand
extern srand
extern timeglobal seedit        ; Seeds the random number generator with a time value
global pull31       ; Pulls a 31-bit random number
global pull16       ; Pulls a 16-bit random number; in the range 0-65,535
global pull8        ; Pulls an 8-bit random number; in the range 0-255
global pull7        ; Pulls a 7-bit random number; in the range 0-127
global pull6        ; Pulls a 6-bit random number; in the range 0-63
global pull4        ; Pulls a (marginal) 4-bit random number; range 0-15
global newline      ; Outputs a specified number of newlines to stdoutpull31: mov ecx,0     ; For 31 bit random, we don't shiftjmp pull
pull16: mov ecx,15      ; For 16 bit random, shift by 15 bitsjmp pull
pull8:  mov ecx,23      ; For 8 bit random, shift by 23 bitsjmp pull
pull7:  mov ecx,24      ; For 7 bit random, shift by 24 bitsjmp pull
pull6:  mov ecx,25      ; For 6 bit random, shift by 25 bitsjmp pull
pull4:  mov ecx,27      ; For 4 bit random, shift by 27 bits
pull:   push ecx        ; rand trashes ecx; save shift value on stackcall rand      ; Call rand for random value; returned in eaxpop ecx            ; Pop stashed shift value back into ECXshr eax,cl       ; Shift the random value by the chosen factor;  keeping in mind that part we want is in CLret           ; Go home with random number in eaxseedit:  push dword 0        ; Push a 32-bit null pointer to stack, since;  we don't need a buffer. call time       ; Returns time_t value (32-bit integer) in eaxadd esp,4     ; Clean up stackpush eax        ; Push time value in eax onto stackcall srand       ; Time value is the seed value for random gen.add esp,4     ; Clean up stackret         ; Go home; no return valuesnewline:mov ecx,10       ; We need a skip value, which is 10 minus thesub ecx,eax        ;  number of newlines the caller wants.add ecx,nl       ; This skip value is added to the address ofpush ecx        ;  the newline buffer nl before calling printf.call printf      ; Display the selected number of newlinesadd esp,4      ; Clean up the stackret         ; Go home
nl  db 10,10,10,10,10,10,10,10,10,10,0  

程序分析:
这个文件包含的几个函数在《32位汇编语言学习笔记(43)--生成随机数》一文中都有分析:
pull函数用于产生规定位数的随机数。
seedit函数用于设置随机数种子的初值。
newline函数用于根据eax值,打印换行符的个数,比如eax=1,就打印一个换行符。

textfile.asm是主程序:

[SECTION .data]          ; Section containing initialised dataIntFormat   dd '%d',0
WriteBase   db 'Line #%d: %s',10,0
NewFilename db 'testeroo.txt',0
DiskHelpNm  db 'helptextfile.txt',0
WriteCode   db 'w',0
OpenCode    db 'r',0
CharTbl     db '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-@'
Err1        db 'ERROR: The first command line argument must be an integer!',10,0
HelpMsg     db 'TEXTTEST: Generates a test file.  Arg(1) should be the # of ',10,0
HELPSIZE    EQU $-HelpMsgdb 'lines to write to the file.  All other args are concatenated',10,0db 'into a single line and written to the file.  If no text args',10,0db 'are entered, random text is written to the file.  This msg  ',10,0db 'appears only if the file HELPTEXTFILE.TXT cannot be opened. ',10,0
HelpEnd     dd 0[SECTION .bss]          ; Section containing uninitialized dataLineCount   resd 1       ; Reserve integer to hold line count
HELPLEN     EQU 72      ; Define length of a line of help text data
HelpLine    resb HELPLEN    ; Reserve space for disk-based help text line
BUFSIZE     EQU 64      ; Define length of text line buffer buff
Buff        resb BUFSIZE+5 ; Reserve space for a line of text [SECTION .text]          ; Section containing code;; These externals are all from the glibc standard C library:
extern fopen
extern fclose
extern fgets
extern fprintf
extern printf
extern sscanf
extern time;; These externals are from the associated library linlib.asm:
extern seedit           ; Seeds the random number generator
extern pull6            ; Generates a 6-bit random number from 0-63
extern newline          ; Outputs a specified number of newline charsglobal main            ; Required so linker can find entry pointmain:push ebp      ; Set up stack frame for debuggermov ebp,esppush ebx        ; Program must preserve EBP, EBX, ESI, & EDIpush esipush edi
;;; Everything before this is boilerplate; use it for all ordinary apps!    call seedit     ; Seed the random number generator;; First test is to see if there are command line arguments at all.;; If there are none, we show the help info as several lines.  Don't;; forget that the first arg is always the program name, so there's;; always at least 1 command-line argument!mov eax,[ebp+8]       ; Load argument count from stack into EAXcmp eax,1      ; If count is 1, there are no argsja chkarg2        ; Continue if arg count is > 1mov ebx,DiskHelpNm ; Put address of help file name in ebx call diskhelp        ; If only 1 arg, show help info...jmp gohome        ; ...and exit the program;; Next we check for a numeric command line argument 1:
chkarg2:    mov ebx,[ebp+12]   ; Put pointer to argument table into ebxpush LineCount      ; Push address of line count integer for sscanfpush IntFormat       ; Push address of integer formatting codepush dword [ebx+4]    ; Push pointer to arg(1)call sscanf     ; Call sscanf to convert arg(1) to an integeradd esp,12     ; Clean up the stackcmp eax,1       ; Return value of 1 says we got a numberje chkdata      ; If we got a number, go on; else abortmov eax,Err1     ; Load eax with address of error message #1call showerr     ; Show the error messagejmp gohome      ; Exit the program;; Here we're looking to see if there are more arguments.  If there;; are, we concatenate them into a single string no more than BUFSIZE;; chars in size.  (Yes, I *know* this does what strncat does...)
chkdata:cmp dword [ebp+8],3    ; Is there a second argument?jae getlns     ; If so, we have text to fill a file withcall randline      ; If not, generate a line of random text; Note that randline returns ptr to line in esijmp genfile      ; Go on to create the file;; Here we copy as much command line text as we have, up to BUFSIZE;; chars, into the line buffer buff. We skip the first two args;; (which at this point we know exist) but we know we have at least;; one text arg in arg(2).  Going into this section, we know that;; ebx contains the pointer to the arg table. All other bets are off.
getlns: mov edx,2       ; We know we have at least arg(2), start theremov edi,Buff      ; Destination pointer is start of char bufferxor eax,eax        ; Clear eax to 0 for the character countercld           ; Clear direction flag for up-memory movsbgrab: mov esi,[ebx+edx*4]    ; Copy pointer to next arg into esi
.copy:  cmp byte [esi],0    ; Have we found the end of the arg?je .next     ; If so, bounce to the next argmovsb            ; Copy char from [esi] to [edi]; inc edi & esiinc eax           ; Increment total character countcmp eax,BUFSIZE        ; See if we've filled the buffer to max countje addnul     ; If so, go add a null to buff & we're donejmp .copy.next: mov byte [edi],' '    ; Copy space to buff to separate argsinc edi            ; Increment destion pointer for spaceinc eax            ; Add one to character count toocmp eax,BUFSIZE     ; See if we've now filled buffje addnul        ; If so, go down to add a nul and we're doneinc edx            ; Otherwise, increment the argument countcmp edx, dword [ebp+8]    ; Compare against argument countjae addnul      ; If edx = arg count, we're donejmp grab      ; And go back and copy itaddnul:    mov byte [edi],0    ; Tuck a null on the end of buffmov esi,Buff        ; File write code expects ptr to text in esi;; Now we create a file to fill with the text we have:
genfile:push WriteCode      ; Push pointer to file write/create code ('w')push NewFilename    ; Push pointer to new file namecall fopen       ; Create/open fileadd esp,8     ; Clean up the stackmov ebx,eax     ; eax contains the file handle; save in ebx;; File is open.  Now let's fill it with text:  mov edi,[LineCount] ; The number of lines to be filled is in edipush esi        ; esi is the pointer to the line of textpush 1          ; The first line numberpush WriteBase       ; Push address of the base stringpush ebx       ; Push the file handle of the open filewriteline:cmp dword edi,0        ; Has the line count gone to 0?je donewrite     ; If so, go down & clean up stackcall fprintf       ; Write the text line to the filedec edi            ; Decrement the count of lines to be writtenadd dword [esp+8],1    ; Update the line number on the stackjmp writeline      ; Loop back and do it again
donewrite:      add esp,16      ; Clean up stack after call to fprintf;; We're done writing text; now let's close the file:
closeit:    push ebx        ; Push the handle of the file to be closedcall fclose       ; Closes the file whose handle is on the stackadd esp,4;;; Everything after this is boilerplate; use it for all ordinary apps!
gohome: pop edi         ; Restore saved registerspop esipop ebxmov esp,ebp      ; Destroy stack frame before returningpop ebpret            ; Return control to to the C shutdown codediskhelp:push OpenCode        ; Push pointer to open-for-read code "r"push ebx      ; Pointer to name of help file is passed in ebxcall fopen       ; Attempt to open the file for readingadd esp,8     ; Clean up the stackcmp eax,0       ; fopen returns null if attempted open failedjne .disk      ; Read help info from disk, else from memorycall memhelp        ret
.disk:  mov ebx,eax     ; Save handle of opened file in ebx
.rdln:  push ebx        ; Push file handle on the stackpush dword HELPLEN   ; Limit line length of text readpush HelpLine       ; Push address of help text line buffercall fgets       ; Read a line of text from the fileadd esp,12       ; Clean up the stackcmp eax,0       ; A returned null indicates error or EOFjle .done       ; If we get 0 in eax, close up & returnpush HelpLine        ; Push address of help line on the stackcall printf     ; Call printf to display help lineadd esp,4     ; Clean up the stackjmp .rdln.done: push ebx        ; Push the handle of the file to be closedcall fclose       ; Closes the file whose handle is on the stackadd esp,4     ; Clean up the stackret         ; Go homememhelp:mov eax,1call newlinemov ebx,HelpMsg       ; Load address of help text into eax
.chkln: cmp dword [ebx],0   ; Does help msg pointer point to a null?jne .show       ; If not, show the help linesmov eax,1      ; Load eax with number of newslines to outputcall newline       ; Output the newlinesret            ; If yes, go home
.show:  push ebx        ; Push address of help line on the stackcall printf     ; Display the lineadd esp,4     ; Clean up the stackadd ebx,HELPSIZE    ; Increment address by length of help linejmp .chkln        ; Loop back and check to see if we done yetshowerr:push eax     ; On entry, eax contains address of error messagecall printf        ; Show the error messageadd esp,4       ; Clean up the stackret         ; Go home; no returned valuesrandline:  mov ebx,BUFSIZE     ; BUFSIZE tells us how many chars to pullmov byte [Buff+BUFSIZE],0  ; Put a null at the end of the buffer first
.loop:  dec ebx         ; BUFSIZE is 1-based, so decrementcall pull6        ; Go get a random number from 0-63mov cl,[CharTbl+eax] ; Use random # in eax as offset into table;  and copy character from table into clmov [Buff+ebx],cl    ; Copy char from cl to character buffercmp ebx,0        ; Are we done having fun yet?jne .loop      ; If not, go back and pull anothermov esi,Buff      ; Copy address of the buffer into esiret            ;   and go home

程序分析:
main:
    push ebp  //保存栈帧指针
 mov ebp,esp  //ebp=esp,保存栈指针作为新的栈帧位置
 push ebx  //保存通用寄存器
 push esi
 push edi

call seedit  //调用seedit函数,设置随机数种子
 
 mov eax,[ebp+8] //命令行参数个数放入eax
 cmp eax,1  //比较eax和1
 ja chkarg2  //如果命令行参数个数大于1,则跳转至chkarg2
 mov ebx,DiskHelpNm //ebx= DiskHelpNm
 call diskhelp  //如果命令行参数个数为1,调用diskhelp函数
 jmp gohome  //退出程序

chkarg2:    //命令行参数个数大于1,会跳转到这
 mov ebx,[ebp+12] //ebx=字符串指针数组地址
 push LineCount  //LineCount变量的地址
 push IntFormat     //格式化字符串地址,作为format参数
 push dword [ebx+4] //第一个命令行字符串参数的地址,作为buffer参数
 call sscanf  //调用sscanf函数,会把argv的第一个字符串参数当做一个整数来处理,保存到LineCount中。
 add esp,12  //清理栈
 cmp eax,1  //比较sscanf函数的返回值和1
 je chkdata  //如果eax=1跳转到chkdata
 mov eax,Err1  //否则eax= Err1
 call showerr  //调用showerr函数,显示出错信息
 jmp gohome  //退出程序

chkdata:
 cmp dword [ebp+8],3 //比较命令行参数的个数与3
 jae getlns  //如果大于等于3,表示有文本需要写入文件,跳转到getlns。
 call randline  //调用randline函数,随机产生一段文本。
 jmp genfile  //跳转到genfile,生成文件。

getlns: mov edx,2  //edx=2
 mov edi,Buff  //edi=Buff,目的缓存
 xor eax,eax  //eax=0
 cld   //内存地址变化方向从低到高

grab: mov esi,[ebx+edx*4] //esi=命令行参数字符串指针(从argv[2]开始)
.copy:  cmp byte [esi],0 //是否是空指针,如果是空指针,说明字符串已经到了结束。
 je .next  //如果是空指针,跳转到next
 movsb  //edi=esi,esi=esi+1,edi=edi+1
 inc eax   //eax=eax+1,字符串的字符数计数
 cmp eax,BUFSIZE  //比较eax和BUFSIZE(避免溢出)
 je addnul  //如果eax等于BUFSIZE,则跳转到addnul,结束拷贝。
 jmp .copy   //跳转到.copy,继续拷贝字符串。
 
.next: mov byte [edi],' ' //拷贝到edi一个空格
 inc edi   //edi=edi+1
 inc eax   //eax=eax+1,加了一个空格所以加1
 cmp eax,BUFSIZE  //比较eax和BUFSIZE(避免溢出)
 je addnul  //如果eax等于BUFSIZE,则跳转到addnul,结束拷贝。
 inc edx   //否则edx=edx+1
 cmp edx, dword [ebp+8]  //edx与命令行参数个数相比较
 jae addnul  //如果edx大于等于命令行参数个数,则跳转到addnul
 jmp grab  //跳转到grab处理下一个命令行参数字符串。

addnul: mov byte [edi],0 //edi=0,字符串用0结束
 mov esi,Buff  //esi = Buff

genfile:
 push WriteCode  // 'w'参数入栈,用于创建和写文件。
 push NewFilename  //文件名参数入栈
 call fopen  //调用fopen函数
 add esp,8  //清理栈
 mov ebx,eax  //文件句柄保存到ebx

mov edi,[LineCount] //把要写的行数装入edi中

push esi  // Buff参数,对应WriteBase的%s
 push 1  //第一行,对应WriteBase的%d
 push WriteBase  //格式化字符串地址
 push ebx  //文件句柄
 
writeline:
 cmp dword edi,0  //比较edi和0
 je donewrite  //如果等于0,表示写完,跳转到donewrite,清理栈
 call fprintf  //调用fprintf函数,把格式化字符串写入到文件。
 dec edi   //edi = edi-1
 add dword [esp+8],1 //更新栈上的行号信息
 jmp writeline  //跳转到writeline,继续循环
donewrite:  
 add esp,16  //清理栈

closeit: 
 push ebx  //文件句柄压入栈
 call fclose  //调用fclose函数
 add esp,4    //清理栈
 
gohome: pop edi   //恢复寄存器
 pop esi
 pop ebx
 mov esp,ebp  //恢复栈指针
 pop ebp         //恢复栈帧指针
 ret

diskhelp:
 push OpenCode  //’r’
 push ebx  // DiskHelpNm文件名
 call fopen  //调用fopen函数,打开文件
 add esp,8  //清理栈
 cmp eax,0    //比较eax和0
 jne .disk  //eax不等于0,说明读取文件成功,跳转到.disk
 call memhelp //读取失败则调用memhelp函数
 ret
.disk: mov ebx,eax  //ebx=eax,保存文件句柄
.rdln: push ebx  //文件句柄压入堆栈(stream)
 push dword HELPLEN //从文件读取的字节数(n)
 push HelpLine  //读入到HelpLine(string)
 call fgets  //调用fgets函数,读入文件数据到缓存
 add esp,12  //清理栈
 cmp eax,0  //比较eax和0
 jle .done  //如果小于等于0跳转到.done(小于等于0意味着出错或读完文件)
 push HelpLine  //HelpLine压入栈
 call printf  //打印帮助信息
 add esp,4  //清理栈
 jmp .rdln     //继续循环

.done: push ebx  //文件句柄压入堆栈
 call fclose  //关闭文件
 add esp,4  //清理栈
 ret

memhelp:
 mov eax,1
 call newline          //打印一个换行符
 mov ebx,HelpMsg  //ebx= HelpMsg
.chkln: cmp dword [ebx],0 //ebx与0比较
 jne .show  //如果不等于0,跳转到.show
 mov eax,1  //eax=1
 call newline  //打印一个换行符
 ret   
.show: push ebx  // ebx压入栈
 call printf  //打印一行帮助信息
 add esp,4  //清理栈
 add ebx,HELPSIZE //ebx=ebx+ HELPSIZE,HELPSIZE是一行帮助信息的长度
 jmp .chkln  //继续循环
 
showerr:
 push eax  //eax保存错误信息字符串地址
 call printf     //打印错误提示信息
 add esp,4  //清理栈
 ret   
 
randline: 
 mov ebx,BUFSIZE  //ebx= BUFSIZE
 mov byte [Buff+BUFSIZE],0  //Buff[BUFSIZE] = 0
.loop: dec ebx   //ebx = ebx -1
 call pull6  //产生6位随机数
 mov cl,[CharTbl+eax] //cl= CharTbl[eax],eax是产生的6位随机数,大小在0到63之间。
 mov [Buff+ebx],cl //保存cl到Buff[ebx]
 cmp ebx,0  //ebx与0比较
 jne .loop  //如果不等于0,继续循环
 mov esi,Buff  //esi=Buff
 ret 
 
makefile文件内容:

textfile: textfile.o linlib.ogcc textfile.o linlib.o -o textfile
textfile.o: textfile.asmnasm -f elf -g -F stabs textfile.asm
linlib.o: linlib.asmnasm -f elf -g -F stabs linlib.asm

测试:

[root@bogon textfile]# make
nasm -f elf -g -F stabs textfile.asm
nasm -f elf -g -F stabs linlib.asm
gcc textfile.o linlib.o -o textfile
[root@bogon textfile]# ./textfile 5 hello world!
[root@bogon textfile]# cat testeroo.txt
Line #1: hello world!
Line #2: hello world!
Line #3: hello world!
Line #4: hello world!
Line #5: hello world!

32位汇编语言学习笔记(45)--测试简单文件操作接口(完)相关推荐

  1. windows下32位汇编语言学习笔记

    windows下32位汇编语言学习笔记 第一章  第一章 背景知识 80x86处理器的存储器 4个数据寄存器 EAX,EBX,ECX,EDX EAX寄存器 所有API函数的返回值都保存在EAX里,注意 ...

  2. 32位汇编语言学习笔记(43)-- 生成随机数

     此程序出自<Assembly Language step by step programming with linux>第12章,用于演示随机数函数的使用,共涉及两个随机数函数: v ...

  3. 32位汇编语言学习笔记(33)--aaa指令

     aaa(ASCII adjust after addition)指令,是BCD指令集中的一个指令,用于在两个未打包的BCD值相加后,调整al和ah寄存器的内容. BCD(Binary-coded ...

  4. 32位汇编语言学习笔记(36)--repne scasb指令

     repne scasb指令,用于扫描字符串,计算字符串的长度,如下两条指令: cld repne scasb 对应的等价指令是: scans:inc edi     dec ecx     je ...

  5. Golang 学习笔记(08)—— 文件操作

    path 在path包中封装了一些路径相关的操作,在开始接触文件操作之前,我们先看看路径的相关函数.在Linux中,路径的格式为/user/bin路径中分隔符是/:Windows中的路径格式为c:\W ...

  6. 16位汇编语言学习笔记(1)——基础知识

    文章目录 1.配置汇编学习环境 1.1 工具下载 1.2 配置环境 2. 汇编命令基础 2.1 简单使用 2.2 常用命令 3. 汇编语言基础 3.1 汇编语言程序与汇编程序 3.2 汇编语言程序的格 ...

  7. 16位汇编语言学习笔记(2)—— 汇编程序设计

    文章目录 4. 顺序程序设计 4.1 十进制的算数运算 4.2 输入输出功能调用 4.3 综合案例 5. 分支程序设计 5.1 转移指令 5.1.1 条件转移指令 单标志条件转移指令 无符号数专用条件 ...

  8. JAVA学习笔记_Junit测试简单使用_assertEquals

    一开始,是通过输出结果判断输出结果是否正确来判断,console虽然输出是true,false 和预期的是一样,但是junit显示都是成功的,并没有出现报错,达不到使用junit测试的效果. juni ...

  9. JAVA学习笔记_Junit测试简单使用_断言assertEquals

    一开始,是通过输出结果判断输出结果是否正确来判断,console虽然输出是true,false 和预期的是一样,但是junit显示都是成功的,并没有出现报错,达不到使用junit测试的效果. juni ...

最新文章

  1. 2012体感发展加速,微软再添新对手
  2. [原]vue实现全选,反选
  3. 打印 指定目录下和子目录下的的所有.java文件的路径. (使用FileFilter过滤器)
  4. Building an MFC project for a non-Unicode character set is deprecated
  5. google Guava包的ListenableFuture解析
  6. 图解Android - Zygote, System Server 启动分析
  7. 《每日一题》290. Word Pattern
  8. 丰厚奖学金博士招生 | 澳大利亚OPTIMA 招募博士,多光谱时间序列数据的时空目标检测/分割方向...
  9. tampermonkey怎么不能用了_iPhone12无线充电不能用怎么办-苹果12无线充电失效原因...
  10. coreboot学习3:启动流程跟踪之bootblock阶段
  11. 复制百度文库的文字加什么后缀_如何复制百度文中的文章.doc
  12. 2018浙江大华股份有限公司-高级C/C++软件开发工程师面试准备
  13. 山特服务器硬盘480g,【02311VHS N480SSDW2SPA 480GB SATA SSD 华为服务器固态硬盘】价格_厂家 - 中国供应商...
  14. win10 2016 长期服务版激活
  15. 大厂职级、薪资一览表,你处在哪一级?(BAT/TMD/华为)
  16. 三极管发射极偏置原理应用于LED驱动电路的分析
  17. excel数据分析 - 39个快捷键&10个操作技巧
  18. Halcon的C++教程
  19. 谷歌chrome浏览器设置成深色(护眼)模式
  20. 微信文章如何采集php,记录微信公众号历史文章采集(二、js代码完善和数据库建立)...

热门文章

  1. C2977 “boost::type_of::encode_type”: 模板 参数太多
  2. SQL Server添加MDW性能监控报表
  3. Basler Blaze-101开发实践(1)——实时采图
  4. 批量修改文件名字、不同的目录下
  5. 三极管工作原理图解,快速了解三极管结构和工作原理
  6. 网站被攻击了,接入CDN防护,源IP是否需要修改
  7. 确定性网络:从“尽力而为”到“确定承诺”
  8. XSL 和 XSLT的区别
  9. java链表源码_JAVA之链表源码 - 飞翔的南极企鹅的个人空间 - OSCHINA - 中文开源技术交流社区...
  10. settings.xml详解