【前言】
这学期开汇编语言课。作为培养方案中的一门选修课,与课组中教微机原理、嵌入式的同事商量,决定这门课就以8086为载体,带大家入门即可。不过,在课程结束之前,也向同学们做了拓展。
本文的版权属于我的2015级学生小鲁同学。他在课程设计阶段,费了不少周折,配置环境,完成了32位汇编程序的开发。他将整个过程做了记录,并且完成本文。
我说:“你将这些文字发成博客做个分享。”他说:“不发了。老师,您的博客点击量大,给您吧。”
这是本文的由来。感谢这位自主、慷慨的弟子!
【正文】
Visual Studio 2015只需要14步就可以运行32位汇编语言。你必须创建一个32位的控制台应用(原本32位控制台应用用于C++编写)并且修改几个地方的设置。
Step1:选择左上角的创建项目,选择创建Win32控制台应用程序。(Depending on your Visual Studio configuration, you might have to find Visual C++ under the “Other Languages” category in the left panel.)当然你的Visual Studio 2015的win32控制选项可能需要你自己找找了,但是Visual Studio 2015必须可以编写并运行C++程序。
Step2:都搞好之后点确定,进行下一步的选择,点击下一步,之后选择空项目。
Step3:点完成保存项目的设置。
Step4:此时方案已经生成,在右边显示了当前的项目名称,接下来需要点击鼠标右键选择生成依赖项,选择生成自定义,此时会出现一个生成自定义文件的选择对话框,选择masm,然后点击确定。
Step5:此时就可以进行基本的80386汇编程序了,在右上方的项目的源文件中添加新建项,创建C++文件,将名称改为*.asm作为汇编程序,之后点击添加。添加一个测试文件试试是否可行。
; AddTwo.asm - adds two 32-bit integers.
; Chapter 3 example
.386
.model flat,stdcall
.stack 4096
ExitProcess proto,dwExitCode:dword
.code
main proc
mov eax,5
add eax,6
invoke ExitProcess,0
main endp
end main
将此段汇编程序写入创建好的mytest.asm文件中,点击上方的生成,生成解决方案,之后观察当前控制台的提示,成功之后就可以点击本地Windows调试器运行了。
Step6:既然是Intel汇编,那么想使用Irvine32头文件中的给出的函数,就需要再加一些配置了,首先需要下载这个文件,下面给出这个安装包的下载地址:http://www.kipirvine.com/asm/examples/Irvine_7th_Edition.msi
下载好之后安装即可,我安装在C盘的默认目录下,即:C:\Irvine。之后进行配置。
Step7:右击项目,选择属性,在弹出的对话框中选择Microsoft Macro Assembly,就会看到如下配置。
Step8:选择General,然后点击Include Paths,点击编辑,将刚刚安装的Irvine的目录写入编辑框中,This tells the assembler where to find files having a filename extension of “.inc”.告诉编辑器去哪里寻找后缀名位inc的文件,输入好点击确定。
Step9:接下来我们想让VS2015生成链接文件,方便我们查看汇编代码对应的机器代码,点击Listing File,点击Assembled Code Listing File点击编辑,输入$(ProjectName).lst,确定。
Step10: 找到连接器,选择输入,在附加依赖库中添加irvine32.lib,注意每个依赖库以分号结尾!
Step11:选择连接器的常规,选择附加库目录,将安装的地址写入,使得连接器可以找到Irvine32.lib这个库文件,之后点击确定。
Step12:选择连接器的调试,将生成调试信息选择为“优化以便于调试(/DEBUG)”
Step13:选择连接器的高级,选择映像具有安全异常处理程序,选择否。
Step 14:Ok,配置完成,试试调用Irvine32头文件是否可行。下面给出一个在Github上托管的代码的实例:
TITLE Programming Assignment #1 (Project01.asm)
; Author: Andrew Pierno
;
; Description: Write and test a MASM program to perform the following tasks:
; 1. Display your name and program title on the output screen.
; 2. Display instructions for the user.
; 3. Prompt the user to enter two numbers.
; 4. Calculate the sum, difference, product, (integer) quotient and remainder of the numbers.
; 5. Display a terminating message.
; EC: Validate second number is less than the first
; EC: Loop until user decides to quit
; EC: Calculates and displays division as floating point number rounded to .001
INCLUDE Irvine32.inc
.data
myName BYTE "Andrew Pierno ", 0
programTitle BYTE "Programming Assignment #1", 0
instructions BYTE "Please enter two numbers, and I'll show you the sum, difference, product, quotient, and remainder.", 0
prompt_1 BYTE "First Number: ", 0
prompt_2 BYTE "Second Number: ", 0
firstNumber DWORD ? ; integer entered by user
secondNumber DWORD ? ; second integer entered by user.
goodBye BYTE "Goodbye",0
equalsString BYTE " = ", 0
sum DWORD ?
sumString BYTE " + ",0
difference DWORD ?
differenceString BYTE " - ",0
product DWORD ?
productString BYTE " * ",0
quotient DWORD ?
quotientString BYTE " / ",0
remainder DWORD ?
remainderString BYTE " remainder ",0
; Extra Credit
EC1prompt BYTE "**EC: This program verifies the second number is less than the first", 0
EC1warn BYTE "The second number must be less than the first!", 0
EC2prompt BYTE "**EC: This program also calculates and displays the quotient as a floating-point number, rounded to the nearest .001", 0
EC2string BYTE "EC: Floating-point value: ", 0
EC2FloatingPoint REAL4 ? ; short real single precision floating point variable
oneThousand DWORD 1000 ; to convert an int to a floating point number rounded to .001 (can be changed to increase or decrease precision)
bigInt DWORD 0 ; represents the floating point number multiplied by 1000
ECremainder DWORD ? ; for floating point creation
dot BYTE ".",0 ; to serve as the decimal place of a floating point number
firstPart DWORD ? ; for the first part of the floating point representation of the quotient
secondPart DWORD ? ; fot the part of the floating point number after the decimal place
temp DWORD ? ; temporary holder for floating point creation
EC3prompt BYTE "EC: Would you like to play again? Enter 1 for YES or 0 for NO: ", 0
EC3explain BYTE "**EC: This program loops until the user decides to quit.", 0
EC3response DWORD ? ; BOOL for user to loop or exit.
.code
main PROC
; Introduction
; This section prints out the instructions and extra credit options
mov edx, OFFSET myName
call WriteString
mov edx, OFFSET programTitle
call WriteString
call CrLf
mov edx, OFFSET EC1prompt
call WriteString
call CrLf
mov edx, OFFSET EC2prompt
call WriteString
call CrLf
mov edx, OFFSET EC3explain
call WriteString
call CrLf
; Get The Data
; This section gets the first and second number and jumps if the user's second number is greater than the first number
; the program will still allow them to loop even if they enter a second number that is greater than the first.
mov edx, OFFSET instructions
call WriteString
call CrLf
; get firstNumber
top:
mov edx, OFFSET prompt_1
call WriteString
call ReadInt
mov firstNumber, eax
; get secondNumber
mov edx, OFFSET prompt_2
call WriteString
call ReadInt
mov secondNumber, eax
; **EC: Jump if second number greater than first
mov eax, secondNumber
cmp eax, firstNumber
jg Warning
jle Calculate
Warning:
mov edx, OFFSET EC1warn
call WriteString
call CrLf
jg JumpToLoop ; jump if secondNumber > firstNumber
Calculate: ; Calculate Required Values
; sum
mov eax, firstNumber
add eax, secondNumber
mov sum, eax
; difference
mov eax, firstNumber
sub eax, secondNumber
mov difference, eax
; product
mov eax, firstNumber
mov ebx, secondNumber
mul ebx
mov product, eax
; quotient
mov edx, 0
mov eax, firstNumber
cdq
mov ebx, secondNumber
cdq
div ebx
mov quotient, eax
mov remainder, edx
; EC floating point representation of quotient and remainder
fld firstNumber ; load firstNumber (integer) into ST(0)
fdiv secondNumber ; divide firstNumber by secondNumber ?
fimul oneThousand
frndint
fist bigInt
fst EC2FloatingPoint ; take value off stack, put it in EC2FloatingPoint
; Display Results
; sum results
mov eax, firstNumber
call WriteDec
mov edx, OFFSET sumString
call WriteString
mov eax, secondNumber
call WriteDec
mov edx, OFFSET equalsString
call WriteString
mov eax, sum
call WriteDec
call CrLf
; difference results
mov eax, firstNumber
call WriteDec
mov edx, OFFSET differenceString
call WriteString
mov eax, secondNumber
call WriteDec
mov edx, OFFSET equalsString
call WriteString
mov eax, difference
call WriteDec
call CrLf
; product results
mov eax, firstNumber
call WriteDec
mov edx, OFFSET productString
call WriteString
mov eax, secondNumber
call WriteDec
mov edx, OFFSET equalsString
call WriteString
mov eax, product
call WriteDec
call CrLf
; quotient results
mov eax, firstNumber
call WriteDec
mov edx, OFFSET quotientString
call WriteString
mov eax, secondNumber
call WriteDec
mov edx, OFFSET equalsString
call WriteString
mov eax, quotient
call WriteDec
mov edx, OFFSET remainderString
call WriteString
mov eax, remainder
call WriteDec
call CrLf
; EC2 Output
mov edx, OFFSET EC2string
call WriteString
mov edx, 0
mov eax, bigInt
cdq
mov ebx, 1000
cdq
div ebx
mov firstPart, eax
mov ECremainder, edx
mov eax, firstPart
call WriteDec
mov edx, OFFSET dot
call WriteString
;calculate remainder
mov eax, firstPart
mul oneThousand
mov temp, eax
mov eax, bigInt
sub eax, temp
mov secondPart, eax
call WriteDec
call CrLf
; Loop until user quits
; prompts the user to enter a 0 or 1 to continue looping.
; if they do want to play again, it takes them to section 'top'
; skipping the instrucitons
; get response for loop
JumpToLoop: mov edx, OFFSET EC3prompt
call WriteString
call ReadInt
mov EC3response, eax
cmp eax, 1
je top ; jump to top if response == 1
; Say Goodbye
mov edx, OFFSET goodBye
call WriteString
call CrLf
exit ; exit to operating system
main ENDP
END main
以同样的方式生成解决方案,这个时候输出正确!可以进行愉快的32位汇编喽!
注意调试断点的使用!红点就是断点。
调试时调出内存管理,利用反汇编进行内存内容的查看,当然也可以把寄存器也调出来:
这个是个人调整之后的调试界面,左下角的寄存器部分也已进行多种选项,显示标志寄存器,左上角有个反汇编,
将里边的内存地址赋值,在内存查看部分可以看到内存的信息。
点击逐步运行,可以看到寄存器的变化,以及标志寄存器,红色代表改变。
程序编译成功之后生成的列表文件和编写的文件在同一目录下。
对列表文件的部分截屏:
最后还差一个变色包,让汇编环境关键字变色。
MASM syntax highlighting
When a text editor uses syntax highlighting, language keywords, strings, and other elements appear in different colors. Visual Studio highlights MASM reserved words and strings, as shown in the following example:
This won’t happen automatically, but you can create a syntax definition file named Usertype.dat that contains MASM keywords. Then when Visual Studio starts, it reads the syntax file and highlights MASM keywords.
There is an interesting third party Visual Studio 2015 extension named Asm-Dude, featuring a rich set of syntax highlighting and code completion features. You can download it from GitHub.
If you decide to use Visual Studio’s built-in MASM syntax highlighter, here are the required steps to set it up:
1) Download the Usertype.dat file (enclosed in a ZIP file) given here to a folder in which you have read/write permissions. If you are using Windows 7, download to My Documents, or C:\temp, or any folder that doesn’t have security restrictions.
2) Copy Usertype.dat to the C:\Program Files\Microsoft Visual Studio 12.x\Common7\IDE folder. In 64-bit windows, Program Files will be named Program Files(x86).
Windows will display a verification dialog before copying the file.
3) Open Visual Studio, select Options from the Tools menu, select Text Editor, and select File Extension. On the right side of the dialog (shown below), enter asm as the extension, select Microsoft Visual C++ from the Editor list, and click the Add button. Click the OK button to save your changes.
Close Visual Studio and restart it. Open your project and display an ASM file. You should see syntax highlighting in the editor. There is a glitch in the highlighting–assembly language comment line starts start with a semicolon, which C++ doesn’t recognize. But this is a simple workaround: add an extra // right after the semicolon, like this, which will cause the comments to appear in their usual green color。
或者给出VS2015变色安装包,不过安装完之后需要在VS2015中更新一下,重启才有效果。