先熟悉nasm 【1】

 注:应该是新浪博客的一个bug:“define”前面加%是显示乱码的,只能把%用100代替,因此下文看到100define时不要被吓到。
-----------------------------------------------------------------------------------------------------------------------------------------
 已经看到第3章“保护模式”,反复读那段“吸引眼球”的pmtest1.asm,总还是雾蒙蒙的感觉。对其中的SECTION,BITS等关键字不甚了解,于是知道要专门熟悉一下nasm汇编了。
 “可能有很多人开始学汇编时用的都是MASM“,作者猜的太对了,我当初学的就是王爽的那本(中国这样的写作者太少了)。于渊提及“学习成本”这个概念,很是赞同,反正AT&T和INTEL风格的汇编语句最终都翻成一样的机器码,那学通一样就够,反正是汇编工具嘛。
 所谓“熟悉一下nasm汇编”,就是用nasm写一些基础的子程序,慢慢体会,同时也累积一个自己的汇编子程序库,就像libc似的。
 
1,100define plus(a,b) ((a)+(b))
  这种格式的宏跟C很像,预定义之后这样使用:mov ax,plus(1,2)
  nasm的宏处理器便会把它翻译成:mov ax,3 准确说是翻译成"mov ax,((1)+(2))"
  至于((a)+(b))为什么要加这么多括号,是防止变量a,b本身又是运算式,加上括号是保证宏替代后,运算次序不被打乱。

2,org是做什么的
 王爽的书里没提到org,但清华大学的那本黄皮书里是有的(和国内大多数书一样,那本书完全不是写给自学者读的)。
--------------------------------------------------------------------------------------

NASM汇编语言汇编BIN格式的程序中,举例说明ORG的意义

ORG 100H
START:
movax,START;        这里相当于 mov ax,100h
……

上面有一个标签 START ,执行mov ax,START时,ax=100h,如果不写ORG 100H,那么就是相当于 ORG0H,这样的话,ax=00h

这就是说,地址引用的数值 = 该地址在文件内的偏移+ org 的数值。
更本质的说,org指令是提前告诉编译器:“你正在编译的这段的代码段将来肯定会被加载到内存的XXX地址处”。编译器据此来计算每个标签(label)对应的物理地址。(这句话是我自己加上来的)

--------------------------------------------------------------------------------------
分割线里就讲得很清楚了,摘自http://hi.baidu.com/chinfs/blog/item/535d0eed42866addb21cb18d.html 仅摘了文章开头,已足够说明含义,且原文后半部分讲的有些绕

3,写一个最原始的echo
 nasm的多行宏是很强大的,现在就用它来实现最原始的echo功能,在屏幕1行1列处显示一串字符。为方便重复利用,代码都写到一个头文件echo.mac里,需要使用时加上句%include "echo.mac"就好了
----------
%ifndef echo_mac      ;这跟c很像,也是为了防止头文件的重复包含
100define echo_mac
%include"esbp.mac"     ;这是我自己写的另一个多行宏,方便把标签地址映射到相应的es,bp寄存器。
%macro echo 1
jmp%%start     ;nasm真是人性化:%%是专门为多行宏定义的一种本地标签,即,你调用这个宏n次,nasm会
                        ;把%%start转化为n个不同的标签,调用n+1次,则转化成n+1个不同的标签。设想没有这个功能
                        ;的话,同一个宏在一段代码的n个位置展开后,就得到n个一模一样的%%start,那是多么恐怖
%%local:db %1
%strlen bytenum%1         ;用C语言描述就是——#define bytenum strlen(%1)
%%start:movcx,bytenum         ;开始为int 10h中断填充相关参数  cx存储要显示的字符串长度,即几个byte
movah,13h         ;int 10h中断的13h号子功能:在指定行列显示es:bp指向的字符串
moval,1h         ;al设置显示输出方式,1表示字符串只含显示字符,其显示属性在bl,显示后,光标位置改变
mov dh,0          ;第0行     
movdl,0         ;第0列
movbh,0         ;0页为显示页
movbl,00000100b         ;红色字符
esbp%%local        ;%%local即字符串所在内存地址,用esbp宏将其映射到es,bp寄存器
int 10h
%endmacro
%endif
----------
这是esbp.mac文件:
----------
%ifndef esbp_mac
100define esbp_mac
%macro esbp 1
;the address passed here must be a 16-bit digit,namely not greaterthan 0xffff
push ax
push dx
mov ax,%1
mov dx,%1
and dx,0x000f
shr ax,4
mov bp,dx
mov es,ax
pop dx
pop ax
%endmacro
%endif
----------
试试效果如何吧,下面的测试文件boot.asm
----------
%include "echo.mac"
org 0x7c00
start:echo 'hello! oranges world'
times 510-($-start) db 0
dw 0aa55h
----------
编译成bin文件,并dd到虚拟软盘的mbr,bochs调试,如图:

先熟悉nasm之一 <wbr> <wbr>2012-8-12
这个石器时代的echo宏就做好啦!

4,稍微增强这个echo的功能,能够将字符串显示到指定行列。下面是echoAtLC.mac
----------
%ifndef echoAtLC_mac
100define echoAtLC_mac
%include "esbp.mac"
%macro echoAtLC 3
jmp %%start
%%local:db %1
%strlen bytenum %1
%%start:mov cx,bytenum
mov ah,13h
mov al,1h
mov dh,%2
mov dl,%3
mov bh,0
mov bl,00000100b
esbp %%local
int 10h
%endmacro
%endif
----------

5,把echo再做强一些,使其能将字符串显示到光标所在位置
思路是:利用int 10h的3号子功能获取光标所在行列,再调用echoAtLC宏就好了。下面是echoUnderCursor.mac
----------
%ifndef echoUnderCursor_mac
100define echoUnderCursor_mac
%include "echoAtLC.mac"
%macro echoUnderCursor 1
;get the line&column cursor locate
push dx
push cx
push ax
push bx
mov ah,3h
mov bh,0
int 10h             ;dx changed,and it storesline,column 
pop bx
pop ax
pop cx                  
echoAtLC %1,dh,dl
pop dx                    ;dxrecovered
%endmacro
----------

6,readCursor_D.mac和setCursor_page_line_column.mac
这两个头文件内的宏,分别用来读取光标信息和设置光标位置,即对int 10h的3号和2号子功能的包装
头文件readCursor_D.mac
----------
;change dx
;dh store line,dl store column
%ifndef readCursor_D_mac
100define readCursor_D_mac
%macro readCursor_D 0
push bx
push ax
push cx
mov bh,0
movah,3h         ;3号子功能的出口参数分别放在放在cx,dx中,cx里的信息我用不上,于是pop覆盖了
int 10h
pop cx
pop ax
pop bx
%endmacro
%endif
----------

头文件setCursor_page_line_column.mac
----------
;change no reg
%ifndef setCursor_mac
100define setCursor_mac
%macro setCursor_page_line_column 3
push ax
push bx
push dx
mov bh,%1
mov dh,%2
mov dl,%3
movah,2h         ;2号子功能就是设置光标的文本坐标的
int 10h
pop dx
pop bx
pop ax
%endmacro
%endif
----------
此类的头文件越写越多,我开始注意统一规范:例如,第一行注释,讲明这个宏执行后是否对寄存器造成影响,如果是,在宏的名字后统一加_D后缀,像比readCursor_D,_D就是说这个宏dangerous。例如,假如这个宏需要的参数多于2个,我就将参数的名字信息附加到宏的名字里,像比setCursor_page_line_column,这样一看就知道要输入哪些参数。例如,宏的名字尽量跟头文件的名字一样。(当然,不包括.mac后缀部分了)

7,实现光标的回车功能 cursorEnter.mac
----------
;change no reg
%ifndef cursorEnter_mac
100define cursorEnter_mac
%include "setCursor_page_line_column.mac"
%include "readCursor_D.mac"
%macro cursorEnter 0
push dx
readCursor_D        ;获取当前光标的行,列信息
incdh         ;将光标的行数值加1,实现换行
setCursor_page_line_column 0,dh,0         ;最后一个参数0,将光标置于第0列,实现回车----->这就实现一个
                                                          ;Enter键的功能啦
pop dx
%endmacro
%endif
-----------

8,将光标初始化到0行0列 echoinit.mac
----------
;change no reg
%ifndef echoinit_mac
100define echoinit_mac
%include "setCursor_page_line_column.mac"
%macro echoinit 0
setCursor_page_line_column 0,0,0
%endmacro
%endif
----------
这个宏看起来似乎很没必要,我后来也觉得如此,直接用setCursor_page_line_column0,0,0简单又直观。只是一开始写这个宏的时候,setCursor_page_line_column.mac还没写出来,是写到echoinit.mac时,才想到去写setCursor_page_line_column 0,0,0的。

9,写一个会自动换行的echon.mac
%ifndef echon_mac
100define echon_mac
%include "./echoUnderCursor.mac"
%include "./cursorEnter.mac"
%macro echon 1
echoUnderCursor %1
cursorEnter        ;在完成输出功能后,将光标设置到下一行的起始处,为一个echon做准备
                           ;这样,“echon”最后面的那个“n”就其义自见了:取\n换行符之意。
%endmacro
%endif
测试以下效果,下面是测试代码boot.asm
----------
%include "echoinit.mac"
%include "cursorEnter.mac"
%include "./echon.mac"
org 0x7c00
start:
echoinit
echon 'hi,this is the first line..'
echon 'the second line..'
echon 'third linenow..'         ;用起来跟linux上shell自带的echo很像!
times 510-($-start) db 0
dw 0aa55h
---------
编译成bin文件,dd到虚拟软盘的mbr,bochs调试,截图:

 收工,这个echon先做到这儿。发现这样写子程序一直接触不到nasm段(SECTION)的概念,还是有些窄,后面要换换思路。
                                                                                                                                                                                                                                      2012,8,12   吉首
原文地址:https://www.cnblogs.com/weiweishuo/p/3082648.html