masm汇编程序格式:

; (cs) ~ code  (ds) ~ data  (ss) ~ stack
assume cs:code, ds:data, ss:stack 

; 编译完后:
; ds:0 ~ ds+10h:0存放程序段前缀(PSP)
; cs:ip初始化为地址标号main
; data段从ds+10h:0开始,注意此时ds不指向data段

data segment ; data是一个地址标号
; db: define byte  dw: define word  dd: define double word
    str db 'Hello World$' ; str是一个数据标号
    ; ((cs)<<4 + 0) ~  ((cs)<<4 + 11) = 'Hello World$'

    arr dw 0123h, 0234h, 0345h, 0456h 
    ; ((cs)<<4 + 12) = 0123h 
    ; ((cs)<<4 + 14) = 0234h  
    ; ((cs)<<4 + 16) = 0345h
    ; ((cs)<<4 + 18) = 0456h

    ; 数据的直接定址表
    addr dd str, arr         
    ; offset操作符取标号的偏移地址,seg操作符取标号的段地址
    ; 等价于addr dw offset str, seg a, offset b, seg b 
    table dw main, sub  ; 子程序的直接定址表
data ends

stack segment
    ; 初始值为0的容量为256个字单元的栈段,初始栈顶(sp)=100h
    dw 256 dup (0)
stack ends

code segment

main: 
    ;;; 初始化栈段
    mov ax, stack   
    mov ss, ax
    mov sp, 100h        
    ; 栈操作 
    push ax             ; (sp)=(sp)-2  ((ss)<<4 + (sp)) = (ax)
    pop bx              ; (ax) = ((ss)<<4 + (sp))  (sp)=(sp)+2
    pushf               ; push flags
    popf                ; pop flags 可以用于修改标志寄存器

    ;;; 初始化数据段
    mov ax, data
    mov ds, ax
    mov bx, 0    

    ;;; 使用assume和以上初始化代码设置好数据段后可以使用数据标号访问数据,不能使用地址标号访问另一个段的数据       
    mov al, str[si]     ; mov al, ds:[si] 
    add arr, ax         ; add ds:[12], ax  代表单元arr可指示单元大小

    ;;; 常见运算
    add ax, 1           ; (ax) += 1

    add al, bl 
    adc ah, bh          ; 带进位加法,这两条指令等价于add ax,bx

    sub ax, 1           ; (ax) -= 1

    sub al, bl
    sbb ah, bh          ; 带借位减法,这两条指令等价于sub ax,bx

    and ax, 011b        ; (ax) &= 011b
    or  ax, 011b        ; (ax) |= 011b
    inc ax              ; (ax) = (ax) + 1
    dec ax              ; (ax) = (ax) - 1
    div byte ptr [bx]   ; (al) = (ax)//((ds)<<4 + (bx))  (ah) = (ax)%((ds)<<4 + (bx))
    div word ptr [bx]   ; (ax) = ((dx)*10000h + (ax))//((ds)<<4 + (bx))  (ax) = ((dx)*10000h + (ax))%((ds)<<4 + (bx))
    mul byte ptr [bx]   ; (ax) *= ((ds)<<4 + (bx))
    mul word ptr [bx]   ; (dx)*10000h + (ax) = (ax) * ((ds)<<4 + (bx))
    ; 逻辑移位
    mov al,01010001b
    mov cl,3            ; 必须用cl设置移位位数
    shr al,cl           ; 右移,左移是shl


    ;;; 内存访问使用ds和bx
    mov ax, [0ffffh]    ; 警告:含义不明(ax) = ffffh 首位为f时必须加0

    mov al, ds:[0]      ; (al) = ((ds)<<4 + 0) 直接寻址
    mov al, [bx]        ; 同上
    mov al, ds:[bx]     ; 同上

    mov al, [bp]        ; (al) = ((ss)<<4 + (bp)) 栈段寻址用bp

 
    mov [bx], al        ; ((ds)<<4 + (bx)) = (al)  寄存器间接寻址
    mov [bx], ax        ; ((ds)<<4 + (bx)) = (al)   ((ds)<<4 + (bx) + 1) = (ah) 小心高位覆盖

    mov ax, [2+bx]      ; (ax) = ((ds)<<4 + (bx) + 2) 寄存器相对寻址(结构体[bx].idata,数组idata[si])
    mov ax, 2[bx]       ; 同上
    mov ax, [bx].2      ; 同上

    mov ax, [bx+si]     ; (ax) = ((ds)<<4 + (bx) + (si)) 基址变址寻址(二维数组[bx][si])
    mov ax, [bx][si]    ; 同上

    mov ax, [bx+si+2]   ; (ax) = ((ds)<<4 + (bx) + (si) + 2) 相对基址变址寻址(表格中的数组[bx].idata[si],二维数组idata[bx][si]
    mov ax, 2[bx][si]   ; 同上
    mov ax, [bx].2[si]  ; 同上
    mov ax, [bx][si].2  ; 同上

    ;;; 标号偏移地址可进行立即数运算
    mov ax, offset main - offset data ; main, data都是地址标号

    ;;; 串传送指令
    cld                 ; (df) = 0 向右传送, clear direction
    std                 ; (df) = 1 向左传送, set direction
    mov cx, 8           ; 根据(df)从(ds)段复制8个字节到(es)段,(df)=0时(si),(di)++,(df)=1时--
    rep movsb           ; s:movsb, loop s
    mov cx, 8           ; 根据(df)从(ds)段复制8个字到(es)段,(df)=0时(si),(di)++,(df)=1时--
    rep movsw           ; s:movsw, loop s

    ;;; 循环
    mov cx, 10          ; 使用cx设定循环次数
s:  ; 循环体
    inc ax 
    loop s              ; (cx) -= 1   if (cx) == 0: then (cs) = seg s  (ip) = offset s

    ;;; 无条件跳转jmp, 有条件跳转jcxz(jmp if cx is zero)
    jmp short main      ; 8位转移 (ip) += main - (ip_next)
    jmp near main       ; 16位段内转移
    jmp far main        ; 段间转移,指令机器码包含段地址和偏移地址 (cs)=seg main  (ip)=offset main
    jmp ax              ; (ip) = (ax)
    jmp word ptr [bx]   ; (ip) = ((ds)<<4 + (bx))
    jmp dword ptr [bx]  ; (ip) = ((ds)<<4 + (bx))  (cs) = ((ds)<<4 + (bx) + 2)
    jcxz main           ; if (cx)==0: then jmp short main
    cmp ax, bx          ; 计算(ax)-(bx),比较结果用(zf)和(cf)表示
    ; 以下跳转指令检测无符号数cmp指令比较结果
    je main             ; (ax)==(bx)
    jne main            ; (ax)!=(bx)
    jb main             ; (ax)<(bx)
    jnb main            ; (ax)>=(bx)
    ja main             ; (ax)>(bx)
    jna main            ; (ax)<=(bx)

    ;;; 调用子程序call
    call sub            ; push IP,  jmp near ptr sub
;   call far ptr sub    ; push CS,  push IP,  jmp far ptr sub
    call ax             ; push IP,  jmp ax
    call word ptr [bx]  ; push IP,  jmp word ptr [bx]
    call dword ptr [bx] ; push CS,  push IP,  jmp dword [bx]

    ;;; 设置中断向量
    mov ax, 0           ; 中断向量表的段为0
    mov es, ax      
    
    ; 字单元 0:n*4 保存n号中断向量偏移地址
    mov word ptr es:[0*4], offset sub
    ; 字单元 0:n*4 + 2 保存n号中断向量段地址
    mov word ptr es:[0*4+2], seg sub

    ;;; 可屏蔽外中断
    sti                 ; (if)=1
    cli                 ; (if)=0

    ;;; 从CMOS RAM时钟信息中读取当前分钟
    mov al, 2           ; 选中2号CMOS RAM
    out 70h, al         ; 在CMOS端口写入2
    in al, 71h          ; 从CMOS端口读出数据(BCD码),0号对应秒,2号对应分,4号对应时,7号对应日,8号对应月,9号对应年

    ;;; 打印字符串
    mov ax,data         
    mov ds,ax           ; 设置数据段
    mov dx,0            ; 设置打印起始位置偏移地址
    mov ah,9            ; 参数9对应打印的中断子程序
    int 21h             ; 触发中断,打印到'$'为止

    mov ah, 4ch         ; 参数4ch对应程序返回的中断子程序
    mov al, 0           ; 返回值
    int 21h             ; 引发21h号中断,返回dos

; 子程序保存恢复现场, 返回ret ref
sub:
    push cx             ; 保存寄存器,入栈
    nop                 ; 1B的指令,什么也不干
    pop cx              ; 恢复寄存器,出栈
    ret                 ; pop IP
;   retf                ; pop IP,  pop CS
;   iret                ; 中断程序返回pop IP,  pop CS,  popf
code ends               ; end segment
end main                ; end main表示程序入口为main段

编译

打开dosbox,输入以下命令

mount c E:\learn_asm
c:
masm filename;
link filename;
debug filename.exe

debug

?              ; 显示帮助信息
r              ; 显示寄存器内容
r ax           ; 修改寄存器ax内容
d ds:0 8       ; 从((ds)<<4+0)开始打印内存内容,打印到8
e ds:0 12 23   ; 从((ds)<<4+0)开始修改内存内容,输入12 23(16进制)
t              ; 运行一步
p              ; 运行一步(不进入)
g cs:0 8       ; 从代码段((cs)<<4+0)开始运行,运行到8
h ax bx        ; 输出16进制加减((ax)+(bx))和((ax)-(bx))
u cs:0 8       ; 反编译,从((cs)<<4+0)到8
a cs:0         ; 从((cs)<<4 + 0) 开始输入汇编指令,并写入内存
q              ; 退出
; 标志寄存器表示
of(OV, NV)
sf(NG, PL)
zf(ZR, NZ)
pf(PE, PO)
cf(CY, NC)
df(DN, UP)

寄存器及其功能:

寄存器(reg):ax,bx,cx,dx,ah,al,bh,hl,ch,cl,dh,dl,sp,bp,si,di

段寄存器(sreg):ds,ss,cs,es

标志寄存器(flags):zf(zero flag), pf(parity flag), sf(sign flag), cf(carry flag无符号数), of(overflow flag有符号数), df(direction flag), 单步中断(类型码为1)标志tf(trap flag), 可屏蔽外中断使能标志if(interrupt enable flag)

ds:数据段寄存器; ss:栈段寄存器;es,ds和ss只能用通用寄存器赋值; cs:代码段寄存器,只能用jmp修改; ip:代码偏移地址寄存器,不能赋值,只能用loop修改; sp:栈顶偏移地址寄存器,可以用立即数赋值; cx:loop指令的计数器; bx,si,di:数据段偏移地址; bp:栈段偏移地址; si,di:偏移地址增量

32位汇编示例程序

新建文件upper_lower.asm,内容如下

include \masm32\include\masm32rt.inc
assume cs:codesg,ds:datasg

datasg segment
    a db 'BaSiC',10,0
    b db 'iNfOrMaTiOn',10,0
datasg ends

codesg segment
start:
    mov ebx, offset a ; first string

    mov ecx,5
s:
    mov al,[ebx]
    and al,11011111b ; lower to upper
    mov [ebx],al
    inc ebx
    loop s

    mov ebx, offset b ; second string

    mov ecx,11
s0:
    mov al,[ebx]
    or al,00100000b ; upper to lower
    mov [ebx],al
    inc ebx
    loop s0

    call main
    exit

main proc
    cls
    print offset a
    print offset b

    ret
main endp
codesg ends
end start

32位汇编编译环境

在www.masm32.com上下载编译器并安装,新建文件makeit.bat,内容如下:

@echo off

    if exist "upper_lower.obj" del "upper_lower.obj"
    if exist "upper_lower.exe" del "upper_lower.exe"

    \masm32\bin\ml /c /coff "upper_lower.asm"
    if errorlevel 1 goto errasm

    \masm32\bin\PoLink /SUBSYSTEM:CONSOLE "upper_lower.obj"
    if errorlevel 1 goto errlink
    dir "upper_lower.*"
    goto TheEnd

  :errlink
    echo _
    echo Link error
    goto TheEnd

  :errasm
    echo _
    echo Assembly Error
    goto TheEnd
    
  :TheEnd

pause

双击文件即可编译,在工作目录下Ctrl+L,输入cmd,输入upper_lower回车即可看到运行结果

生成C程序的汇编列表文件

gcc -Wa,-adhln -g source_code.c > assembly_list.lst

参数说明:

-g: 生成debug信息
-Wa,option: 把`option`作为参数传给汇编器
-adhln:
a: 生成列表
d: 忽略debug指令
n: 忽略列表处理
h: 包含高级语言代码include high-level source
l: 包含汇编代码

标签: none

评论已关闭