I no longer care about using Irvine or Visual Studio. Screw Visual Studio. KILL Visual Studio!
That being said, lets begin :bgrin:
I'm an absolute beginner. My first priority is learning how to properly use basic op-codes. That being said, hutch--, I appreciate your neat print out code here: https://masm32.com/board/index.php?msg=54890
But it's a little too advanced for me right now. Visual Studio did such a nice job displaying everything by calling Dumpregs, and I was expecting something similar to happen by following dedndave's instruction here:
Quote from: dedndave on February 05, 2016, 06:19:25 AMdowload OllyDbg - as i recall, installation is merely a matter of creating a folder and placing the program in it
you want to make Olly the "default just-in-time" handler
if you open Olly, there is an Options menu
under Debugging, Just-in-time - Set OllyDbg button
it takes care of it for you, and saves previous settings if you want to reverse it
now, any program that generates an exception will cause Olly to pop up
we generally insert
int 3
into the code where we want execution to generate an exception
And I added the
int 3
into the MyTest.asm file:
include \masm32\include\masm32rt.inc
.data
MyArray dd 25, 18, 23, 17, 9, 2, 6
HelloW$ db "Hello World", 0
.code
start:
xor ebx, ebx ; set two non-volatile
xor esi, esi ; registers to zero
.Repeat
add esi, MyArray[4*ebx]
inc ebx
.Until ebx>=lengthof MyArray
MsgBox 0, cat$(str$(esi), " is the sum"), offset HelloW$, MB_OK
int 3
exit
end start
But none of Olly's features happened like he promised. Any suggestions?
Fixed link above, originally it opened a "quote" editor window.
I'd recommend you to use VKDEBUG .
QEDITOR -> Help -> vkDebug
Read it .
All you need to start using it is :
include \masm32\include\debug.inc
includelib \masm32\lib\debug.lib
Now you may want to find out which function dumps the contents of CPU to DbgWin and try it .
Quote from: GoneFishing on February 05, 2016, 07:23:32 PMNow you may want to find out which function dumps the contents of CPU to DbgWin and try it.
deb (http://www.webalice.it/jj2006/MasmBasicQuickReference.htm#Mb1019) is more powerful
*), and easier to use. Anyway, DbgWin commands are described in \Masm32\help\VKDebug.chm
However, Olly would be much more helpful, therefore: What exactly doesn't work with Olly?
- do you have the file \Masm32\OllyDbg\ollydbg.exe ?
- when you start Olly, and open the exe produced with qEditor, what do you see?
Below is a screenshot of Olly. This is what you
should see if you followed the instructions.
*) same program using deb:
include \masm32\MasmBasic\MasmBasic.inc
; include \masm32\include\masm32rt.inc
.data
MyArray dd 25, 18, 23, 17, 9, 2, 6
HelloW$ db "Hello World", 0
.code
start:
xor ebx, ebx ; set two non-volatile
xor esi, esi ; registers to zero
.Repeat
deb 4, "adding: ", MyArray[4*ebx]
add esi, MyArray[4*ebx]
deb 4, "new sum:", esi
inc ebx
.Until ebx>=lengthof MyArray
Inkey Str$("The sum is %i", esi), Str$(", and the counter arrived at %i", ebx)
exit
end start
Output:
adding: MyArray[4*ebx] 25
new sum: esi 25
adding: MyArray[4*ebx] 18
new sum: esi 43
adding: MyArray[4*ebx] 23
new sum: esi 66
adding: MyArray[4*ebx] 17
new sum: esi 83
adding: MyArray[4*ebx] 9
new sum: esi 92
adding: MyArray[4*ebx] 2
new sum: esi 94
adding: MyArray[4*ebx] 6
new sum: esi 100
The sum is 100, and the counter arrived at 7
Red,
The trick is not to take on too much in one go, get used to things a bit at a time. The MASM32 library and macro system is there to get you up and going. Once you learn enough you can start to do the fancy stuff. You need to learn the basics of intel mnemonics, the complex addressing mode and the bare minimum of Windows API functions. Just take it a bit at a time and it will all come together. I wrote the 2 simple procedures on register contents so you had a simple and easy to use way of seeing what you registers held. Once you learn the basics you can start on a debugger if you need it but a lot of code can be written before you need to learn a debugger.
Hutch,
I fully agree that the Masm32 library is the best invention ever after the wheel, but to start learning and understanding assembly, there is nothing simpler than opening an exe in Olly and pressing F8 repeatedly.
Red,
The trick with the 2 procedures I posted is you don't have to understand them in a hurry, they work as they are by simply calling them. Once you know a bit more you will know what they are doing, both copy all of the registers to some memory based variables, then they are displayed using a couple of different macros, one for console, the other for joining all the results and shown in a message box. Many have tried to start in assembler by trying to learn all of the low level basics but it rarely ever works well as there is so much to start with.
The reason for the MASM32 library and the macro support is because MASM does not have a run time library so one was created to make starting with MASM a lot easier. If you use the runtime library and the macros you will get a lot more code up and running and this will make learning the important stuff like mnemonic coding a lot easier. A lot of API functions are just hack OS code to interact with the OS but direct mnemonic code is where the real action is and its where you get some real speed once you properly understand it.
that's a big step in the right direction :t
you will find that the masm32 package is a much faster way to get serious about writing windows-32 code
however, don't discount Kip Irvine's book, altogether
it does a fair job of explaining data types, registers, and instructions
the problem is to be able to take one of Kip's example programs, and convert it to masm32
to understand this "translation", a little background might be helpful....
in the days of 16-bit DOS code, much of the hardware was accessed through software and hardware interrupts
hardware generated physical interrupts that the 8088 "detected", causing specific code to be executed
for example, when a timer interrupt occured, a timer routine in BIOS was executed to update a clock count
software interrupts were "called" by programs using the INT instruction
in most cases, arguments were passed to the interrupts via registers
so, to open a file, for example, you would load AX, CX, and DS:DX with specific values, then INT 21h (DOS function dispatcher)
mov ah,3Ch ;INT 21h function number to create a file
mov cl,<file attributes>
mov dx,offset filename ;assuming that DS points to the proper data segment
int 21h
if the function is successful, the INT returns with the carry flag cleared
if there is an error, the carry flag is set, and AX holds the error code
for some functions, values were also returned in other registers
that is old stuff that you really don't want or need to learn
but, i wanted you to see how calls were made
program functions were often written to be called in much the same way
Kip Irvine (and several others) wrote books, based on these methods
when the transition to 32-bit windows code was made, Kip adapted his old code to work in the new world
windows was designed with a whole new set of "rules"
however, the code examples in Kip's book don't obey the new rules
and, that is where the problems begin - lol
enough history lesson
to work with windows, you will want to understand parts of the windows ABI (application binary interface)
the ABI describes many things, like how object modules are linked and so on
it also gives a basic set of rules for how operating system functions are called, and how they return information:
rather than passing arguments in registers, they are passed on the stack
EBX, EBP, ESI, and EDI registers are preserved across calls
EAX, ECX, and EDX are volatile (may be destroyed)
EAX is often used to return a status or result (not the carry flag, like DOS)
(if a function requires more space to return results, it is usually passed the address of a data structure to fill in)
the direction flag is normally left cleared (up direction)
if you set it (down direction) for your code, you should clear it when done
many operating system functions require the direction flag to be cleared prior to call
the key is, that we generally write our own functions to behave the same way - to follow the same rules
that way, they are interchangable with OS functions
in cases where the ABI must be observed, our own functions obey the rules
i think it's correct to say that all of Hutch's functions in the masm32 library follow the ABI rules
that having been said, there are ways to take Kip's examples and make them masm32 compatible
when one of Kip's "WriteXxxxx" functions are called, you can generally replace it with the masm32 "print" macro
understand that Kip's routines pass arguments in registers - and that he has a tendancy to preserve most registers across calls
very different from the windows ABI
what you really want to do is to be able to follow his code to learn instructions
then, know how to make it work in the simpler world of masm32 code :biggrin:
Quote from: jj2007 on February 05, 2016, 07:56:46 PM
However, Olly would be much more helpful, therefore: What exactly doesn't work with Olly?
- do you have the file \Masm32\OllyDbg\ollydbg.exe ?
- when you start Olly, and open the exe produced with qEditor, what do you see?
I didn't realize I needed to open the MASM32-Editor-produced .exe file in Olly. My bad. I also now understand that pressing F8 steps through (debugs) lines of .asm code one line at a time, kind of like Visual Studio's debugger.
But what exactly was the point of dedndave adding
int 3
to the code?
Also, I couldn't see the Flag statuses anywhere in Olly when I ran it (see my attached screen shot, and help me find them please).
NOTE: the .asm code that generated the .exe file used by Olly in the attached screen shot is:
include \masm32\include\masm32rt.inc
.data
MyArray dd 25, 18, 23, 17, 9, 2, 6
HelloW$ db "Hello World", 0
.code
start:
xor ebx, ebx ; set two non-volatile
xor esi, esi ; registers to zero
.Repeat
add esi, MyArray[4*ebx]
inc ebx
.Until ebx>=lengthof MyArray
MsgBox 0, cat$(str$(esi), " is the sum"), offset HelloW$, MB_OK
exit
end start
One more thing: can someone please paste the code for a simple .asm template that will output straight to a console window or dialog box automatically when run with MASM32 Editor (instead of outputting a console-dialog-box-hybrid that the code above does)? Thanks again.
this should answer 2 of the questions/requests
;###############################################################################################
INCLUDE \Masm32\Include\Masm32rt.inc
.686p
.MMX
.XMM
;###############################################################################################
; .DATA
;***********************************************************************************************
; .DATA?
;###############################################################################################
.CODE
;***********************************************************************************************
main PROC
print "Hello "
int 3 ;generate exception to bring up OllyDbg
print "World!",13,10
inkey
INVOKE ExitProcess,0
main ENDP
;###############################################################################################
END main
Quote from: RedSkeleton007 on February 07, 2016, 04:54:16 AMBut what exactly was the point of dedndave adding int 3
to the code?
Also, I couldn't see the Flag statuses anywhere in Olly when I ran it (see my attached screen shot, and help me find them please).
...
One more thing: can someone please paste the code for a simple .asm template that will output straight to a console window or dialog box automatically when run with MASM32 Editor (instead of outputting a console-dialog-box-hybrid that the code above does)? Thanks again.
- the point of int 3: If you press F9, Olly runs the code until it hits int 3. Useful when your code grows bigger.
- the flags are under the registers in the right panel:
C0
P1
A0
Z1
S0
- template:
include \masm32\include\masm32rt.inc
.code
start:
nop
inkey "hello world"
exit
end start
this is a picture of the Olly window when my little program above is executed
circled in red is the EFL (EFlags) register
notice that Olly came up when the INT 3 was executed
Quote from: dedndave on February 07, 2016, 05:43:45 AM
circled in red is the EFL (EFlags) register
Thanks, but I was looking for Status Flags. Also, why doesn't the number in Olly's EDX register (attached screen shot image) match the prediction comment in my code:
include \masm32\include\masm32rt.inc
.data
val1 WORD 1000h
val2 WORD 2000h
.code
start:
; Demonstrate MOVZX instruction:
mov bx,0A69Bh
movzx eax,bx ; EAX = 0000A69Bh
movzx edx,bl ; EDX = 0000009Bh predicted value
movzx cx,bl ; CX = 009Bh
nop
; inkey "hello world"
; inkey EDX
exit
end start
Also, how do you display attached images between text and stuff, inline with posts?
Quote from: RedSkeleton007 on February 09, 2016, 10:45:50 AMI was looking for Status Flags.
These
are the status flags.
QuoteAlso, why doesn't the number in Olly's EDX register (attached screen shot image) match the prediction comment in my code
Because you forgot to hit F8 until you reach the point of your prediction.
Ready to have some fun? The questions are in the comments of the following code:
include \masm32\include\masm32rt.inc
.data
val1 WORD 1000h
val2 WORD 2000h
strDuke BYTE "Duke Nukem" ; should be 10 bytes, right?
.code
start:
; Lets find out what inkey can do:
inkey strDuke ; when run, this statement causes the program to crash. Why?
inkey "It's time to kick ass and chew bubble gum..."
mov eax,SIZEOF strDuke
; Lets also try out xchg:
mov si,val1 ; SI = 1000h
xchg si,val2 ; SI = 2000h, val2 = 1000h
mov val1,si ; SI = 2000h
; NOTE: SI is from the extended source index register ESI
nop ; what is nop supposed to do?
inkey "and I'm all out of gum."
exit
end start
Also, in the attached screen shot, despite hitting F8 as many times as Olly let me, the value in register SI (of ESI) stayed the same 00000000. Why?
Which book are you using? "The Art of Spoon-Feeding"?
Red,
Why the hell are you working in 16 bit ?
inkey strDuke
the inkey macro wants an address
"strDuke" will likely try to use the first 4 bytes of the string as an address
the address is probably invalid, and that causes the crash
try this
inkey offset strDuke
strDuke BYTE "Duke Nukem" ; should be 10 bytes, right?
it is 10 bytes, but you really don't care
and - strings should be null-terminated
strDuke BYTE "Duke Nukem",0 ;11 bytes total
it may be the lack of a null-terminator that causes it to crash
i am not sure how the macro interprets the label without "offset", because i've never used it that way - lol
Quote from: hutch-- on February 09, 2016, 06:50:29 PM
Why the hell are you working in 16 bit ?
If you say that, my best guess is that you're talking about this part:
mov si,val1 ; SI = 1000h
xchg si,val2 ; SI = 2000h, val2 = 1000h
mov val1,si ; SI = 2000h
; NOTE: SI is from the extended source index register ESI
Since val1 and val2 are only 4 digits, I figured they're 16 bit, and thus could fit into the first 4 LSB portion of the ESI register (or at least, that's my assumption to why the code example in the book shows it that way).
Quote from: dedndave on February 09, 2016, 09:50:12 PM
strDuke BYTE "Duke Nukem" ; should be 10 bytes, right?
it is 10 bytes, but you really don't care
and - strings should be null-terminated
strDuke BYTE "Duke Nukem",0 ;11 bytes total
it may be the lack of a null-terminator that causes it to crash
i am not sure how the macro interprets the label without "offset", because i've never used it that way - lol
Thank you dedndave, that actually does make sense.
in 32-bit code, using word registers is generally slower than using full registers
the opcodes for word register instructions include an extra prefix byte to indicate the "non-native" size
byte register instructions still have unique opcodes (just like 16-bit code), so do not require the override byte
many operations are best performed on full-size registers
and, when memory operands are concerned, it is helpful if they are DWORD-aligned
(i.e., the address for a DWORD operand is evenly divisable by 4)
There are 8 general purpose registers:
EAX, EBX, ECX, EDX, ESI, EDI, ESP, and EBP.
Now, are they strictly designated to specific purposes, or does MASM allow me to each register for whatever I want?
Also, I have a few errors and a question in my code:
include \masm32\include\masm32rt.inc
.data
val1 WORD 1000h
val2 WORD 2000h
strDuke BYTE "Duke Nukem",0 ; 11 bytes
.code
start:
; First, lets find out what inkey can do:
inkey OFFSET strDuke
inkey "It's time to kick ass and chew bubble gum..."
; Next, lets figure out how to move the amount of bytes in
; our string into a register, so that we can use it to
; observe that information using the displayed registers
; in Olly:
mov edi, SIZEOF strDuke
; Finally, lets learn how to use the xchg opcode, so that
; val1 and val2 trade values:
mov eax, val1 ;error: instruction operands must be the same size
xchg eax, val2 ;error: instruction operands must be the same size
mov val1, eax ;error: instruction operands must be the same size
nop ; what is nop supposed to do?
inkey "and I'm all out of gum."
exit
end start
Note that the inkeys worked fine when I ran the program before attempting to add in the xchg stuff.
Red,
have a look in the masm32 help files, the Intel ABI is explained there and its a lot easier to point you there than repeat it all as a post.
the problem is...
the register operand EAX is a DWORD size
the memory operand val1 is a WORD size
they must be the same size (change val1 to a DWORD)
this is true for most instructions
there are a few exceptions, like MOVSX or MOVZX, which convert bytes or words to dwords
ESP is fairly dedicated to the stack
the other 7 can be used in many ways
but, each one has special characteristics
often, these characteristics are due to specific instruction usage
MOVS, MUL, DIV are a few good ones to study
historically:
AX - accumulator
BX - base index address
CX - count
DX - data
SI - source index address
DI - destination index address
BP - base pointer (stack)
Quote from: dedndave on February 10, 2016, 10:02:48 PM
in 32-bit code, using word registers is generally slower than using full registers
the opcodes for word register instructions include an extra prefix byte to indicate the "non-native" size
byte register instructions still have unique opcodes (just like 16-bit code), so do not require the override byte
The override byte? Does the movzx instruction have anything to do with that?
Quote from: dedndave on February 10, 2016, 10:02:48 PM
many operations are best performed on full-size registers
and, when memory operands are concerned, it is helpful if they are DWORD-aligned
(i.e., the address for a DWORD operand is evenly divisable by 4)
It seems my code worked:
include \masm32\include\masm32rt.inc
.data
val1 DWORD 1000h
val2 DWORD 2000h
.code
start:
mov eax, val1 ; would movzx be necessary here?
xchg eax, val2
mov val1, eax
nop ; what is nop supposed to do?
inkey "End of program."
exit
end start
In Olly, as I hit F8, I saw the EAX register change to 00001000 and 00002000 ;)
I found it odd, though, that I didn't have to use movzx, or transfer 16-bit values into 32-bit registers. I guess something in modern 32-bit MASM puts in the first four unused higher bits for you?
One other thing. In Olly, once I've hit F8 enough times to get to the end of stepping through my program, I don't know how to go back. How do I go back to the first step instead of having to close and reopen Olly, and then reopen my program in Olly?
Quote from: RedSkeleton007 on February 25, 2016, 06:54:19 PMThe override byte? Does the movzx instruction have anything to do with that?
No.
Quotemov eax, val1 ; would movzx be necessary here?
No. Both eax and val1 are DWORD size, i.e. 4 bytes long.
Quotenop ; what is nop supposed to do?
\Masm32\help\opcodes.chm
QuoteIn Olly, once I've hit F8 enough times to get to the end of stepping through my program, I don't know how to go back. How do I go back to the first step
Ctrl F2.
in the original post, i think he had val1 defined as a WORD
so, MOVZX or MOVSX could be used
MOVZX zero-extends the value (all the upper bits are set to 0)
MOVSX sign-extends the value (the upper bits are a copy of bit 15 for words or bit 7 for bytes)
typically, we would just define val1 as a DWORD, though
The book does a shitty job explaining how to exchange array elements :( The following code is untested:
include \masm32\include\masm32rt.inc
.data
val1 DWORD 1234h
val2 DWORD 5678h
temp DWORD ?
temp2 DWORD ?
arrayD DWORD 1000h, 2000h, 3000h, 4000h
.code
start:
mov eax, [arrayD+8] ; move array element [2] into eax
xchg temp, eax ; hopefully, eax is empty for the next mov?
mov eax, [arrayD+4] ; move array element [1] into eax
xchg temp2, eax
mov [arrayD+4], temp
mov [arrayD+8], temp2
;arrayD should now be: 1000h, 3000h, 2000h, 4000h
nop ; no operation to do here.
inkey "End of program."
exit
end start
Also, inkey has proven to work great for outputting hard-coded statements into the console, but what is the syntax for outputting a specific array element value into the console? Olly is cool, but at my current level, it's just giving me a headache. Is Olly really necessary right now, when I'm struggling to learn just the basic opcodes?
i troubleshoot most programs without olly - but, it's nice when you need it
if you want to dislpay a value in hex, signed decimal, unsigned decimal...
print uhex$(dword ptr [ebx+4])
print str$(dwVariableName),13,10
print ustr$(eax)
notice i added a carriage return/line feed to one of them
Red,
Olly is an OK debugger but you are better to learn the basics first, once you have that under your belt you can add whatever toys you like but the idea is to keep it simple at first or you get tangled in the sheer complexity of trying to do too many things. One of the tricks is to set yourself up a simple test piece and experiment with the instructions and learn how the "complex addressing mode" works, it looks messy but once you understand it, it becomes really easy to use.
Once you are up and going we will show you the FAST way to exchange memory operands, XCHG is both old and slow and can be done a lot quicker, the way its done in fast sort algos.
Quote from: dedndave on February 26, 2016, 01:16:17 PM
notice i added a carriage return/line feed to one of them
Why? I understand the purpose of null-terminating a string, but what is the purpose of the so-called "carriage return/line feed" instruction?
Quote from: hutch-- on February 26, 2016, 01:21:03 PM
Once you are up and going we will show you the FAST way to exchange memory operands, XCHG is both old and slow and can be done a lot quicker, the way its done in fast sort algos.
So I should completely forget about exchanging values in memory or registers for now?
Also, is this what you mean by complex addressing mode:
http://masm32.com/board/index.php?action=post;quote=254;topic=87.0;last_msg=352 (http://masm32.com/board/index.php?action=post;quote=254;topic=87.0;last_msg=352)
The added 13, 10 gives you a carriage return and a line feed. Much the same as pressing the enter key.
RE: exchanges of values, you will do this on a needs basis, have a good look at both the help files in MASM32 AND the examples and tutorials, this is what they were written for, to help folks learning assembler. If you can get the swing of the addressing mode and some practice using instructions, the rest will come with practice.
And the quest for simple ways of printing register values in the console continues. I've tested the following code (my questions are in the comments as always):
include \masm32\include\masm32rt.inc
.data
val1 DWORD 1000h
val2 DWORD 2000h
.code
start:
mov eax, val1
xchg eax, val2
mov val1, eax ; Wait, if we're removing the value from eax into var1, what's left in eax afterwards?
;print str$(dwVal1),13,10 ; error: INVOKE argument type mismatch (is str$ the problem?)
;print ustr$(eax),13,10 ; result was 8192. Why? That can't be in hex.
;print str$(eax) ; result was 8192 again. And why did using str$ work this time?
inkey "We've made it through the operations without errors!" ; yes, we do make it this far and past!
nop ; no operation left to perform
inkey "End of program."
exit
end start
mov val1, eax ; Wait, if we're removing the value from eax into var1, what's left in eax afterwards?
;print str$(dwVal1),13,10 ; error: INVOKE argument type mismatch (is str$ the problem?)
;print ustr$(eax),13,10 ; result was 8192. Why? That can't be in hex.
;print str$(eax) ; result was 8192 again. And why did using str$ work this time?
1) the source register is unchanged in MOV
2) dwVal1 is not defined - try val1
3) ustr$ converts to unsigned decimal string - 8192 decimal is 2000h
4) i think str$ is the same as ustr$
...and the masm32 package has a bug with regard to ustr$
http://masm32.com/board/index.php?topic=1811.0 (http://masm32.com/board/index.php?topic=1811.0)
i proposed a fix here, with no disagreements - lol
http://masm32.com/board/index.php?topic=1811.msg33372#msg33372 (http://masm32.com/board/index.php?topic=1811.msg33372#msg33372)
Quote from: dedndave on March 02, 2016, 09:48:54 PM
2) dwVal1 is not defined - try val1
I still get the same error.
Quote from: dedndave on March 02, 2016, 09:48:54 PM
3) ustr$ converts to unsigned decimal string - 8192 decimal is 2000h
Okay, so how do I get it to print out in hexadecimal? I would prefer to see my output in the same format as my input.
Quote from: RedSkeleton007 on March 03, 2016, 05:36:44 AMOkay, so how do I get it to print out in hexadecimal?
include \masm32\include\masm32rt.inc
.code
start:
print hex$(2000h)
exit
end start
Always at your service 8)
(http://www.youth.ly/wp-content/uploads/2013/11/202191RKERGB75-e1384804892369-620x320.jpg)
When I try to print both the value in a register and in a variable at the same time:
include \masm32\include\masm32rt.inc
.data
val1 DWORD 1000h
val2 DWORD 2000h
.code
start:
mov eax, val1 ; eax should now have 1000h
xchg eax, val2 ; eax should now have 2000h, and val2 should now be 1000h instead of 2000h
mov val1, eax ; val1 should now have 2000h instead of 1000h
; val1 and eax should both be 2000h:
print hex$(val1),13,10 ; result: 00002000
print hex$(eax),13,10 ; result: 00000002
inkey "We've made it through the operations without errors!"
nop ; no operation left to perform
inkey "End of program."
exit
end start
it prints out two different values. Strangely, the value 00002000 comes out when I only print one of them (from eax or val1). What is going on?
Quote from: RedSkeleton007 on March 03, 2016, 08:31:39 AM
it prints out two different values. Strangely, the value 00002000 comes out when I only print one of them (from eax or val1). What is going on?
See here (http://www.webalice.it/jj2006/Masm32_Tips_Tricks_and_Traps.htm), read the part beginning with "Assembly beginners stumble"...
Quote from: jj2007 on March 03, 2016, 08:56:08 AM
See here (http://www.webalice.it/jj2006/Masm32_Tips_Tricks_and_Traps.htm), read the part beginning with "Assembly beginners stumble"...
Read. Is it worth my time as a noob to do research on why registers get trashed, or should I just accept that truth and move on?
Red,
Its called the "Intel Application Binary Interface" (ABI) and it is how all 32 bit Windows API code is written. If you properly observe the ABI design, you will not have problems with mysterious register overwrites, this is why the system is designed this way. If you go to the help file "ASM Intro Help" and go to the link "Register Preservation Convention" you will see how it works. It looks complicated but is really simple in operation.
include \masm32\include\masm32rt.inc
.data
val1 DWORD 1000h
.code
start:
mov eax, val1
print hex$(val1),13,10
print hex$(eax),13,10
inkey
mov eax, val1
push eax
print hex$(val1),13,10
pop eax
print hex$(eax),13,10
inkey
exit
end start
hex$ MACRO DDvalue
LOCAL rvstring
.data
rvstring db 12 dup (0)
align 4
.code
invoke dw2hex,DDvalue,ADDR rvstring
EXITM <ADDR rvstring>
ENDM
; ########################################################################
;
; This original module was written by f0dder.
;
; Part of the code has been optimised by Alexander Yackubtchik
;
; ########################################################################
.386
.model flat, stdcall
option casemap :none ; case sensitive
.code
; ########################################################################
; THIS CODE CONTAINS 6 INSTRUCTIONS THAT MODIFY, OR POTENTIALLY MODIFY,
; EAX WITHOUT PRESERVING ITS VALUE, IN PERFECT CONFORMANCE WITH THE ABI.
dw2hex proc source:DWORD, lpBuffer:DWORD
push esi
mov edx, lpBuffer
mov esi, source
xor eax, eax
xor ecx, ecx
mov [edx+8], al ; put terminator at correct length
mov cl, 7 ; length of hexstring - 1
@@:
mov eax, esi ; we're going to work on AL
and al, 00001111b ; mask out high nibble
cmp al,10
sbb al,69h
das
mov [edx + ecx], al ; store the asciihex(AL) in the string
shr esi, 4 ; next nibble
dec ecx ; decrease counter (one byte less than dec cl :-)
jns @B ; eat them if there's any more
pop esi
ret
dw2hex endp
; #########################################################################
end
00001000
00000002
Press any key to continue ...
00001000
00001000
Press any key to continue ...
This means that if you must store values in any of the 3 mentioned registers and then call other procedures, you must preserve any of EAX ECX EDX before you call another procedure and restore them after it has returned.
So how do I get MASM to preserve the values in those three registers? Also, I'm not really sure what "restoring them" means.
The solution is to use LOCAL variables to get the procedure running reliably then selectively replace them with any registers that may not be used in the procedure. With careful register reuse you can reduce the number of LOCAL variables by using registers and produce faster code if you get it right.
I'm assuming that variables are local to the procedure that they're declared and used in. So how do you get MASM to treat variables as global? For example, are val1 and val2 considered global variables in the following code:
include \masm32\include\masm32rt.inc
.data
val1 DWORD 1000h
val2 DWORD 2000h
.code
start:
mov eax, val1 ; eax should now have 1000h
xchg eax, val2 ; eax should now have 2000h, and val2 should now be 1000h instead of 2000h
mov val1, eax ; val1 should now have 2000h instead of 1000h
print hex$(val1),13,10 ; result: 00002000
;What must I do to preserve eax, so that the result stays "00002000"?
print hex$(eax),13,10 ; result: 00000002
inkey "We've made it through the operations without errors!"
nop ; no operation left to perform
inkey "End of program."
exit
end start
push = preserve
pop =restore
Ensure you pop registers in REVERSE order to what you push them in.
Like this
push ebx
push esi
push edi
; run your code here
pop edi
pop esi
pop ebx
Red,
Here is a simple test piece that shows how to use the old instruction XCHG. You would not normally write it this way but it is done so you can understand it.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
comment * -----------------------------------------------------
Build this template with
"CONSOLE ASSEMBLE AND LINK"
----------------------------------------------------- *
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
inkey
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
LOCAL var1 :DWORD
LOCAL var2 :DWORD
push esi
push edi
mov var1, 12345678
mov var2, 87654321
print "Before XCHG",13,10
print str$(var1)," var1",13,10
print str$(var2)," var2",13,10,13,10
mov esi, var1
mov edi, var2
; ***************************
xchg esi, edi ; exchange the two registers
; ***************************
mov var1, esi
mov var2, edi
print "After XCHG",13,10
print str$(var1)," var1",13,10
print str$(var2)," var2",13,10
pop edi
pop esi
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
Quote from: hutch-- on March 05, 2016, 12:21:16 AM
main proc
LOCAL var1 :DWORD
LOCAL var2 :DWORD ;Why didn't you need to use [b]?[/b] to indicate that the variables are uninitialized?
push esi ;Why are you pushing a register onto the stack before storing var1 or var2 in it?
push edi
mov var1, 12345678
mov var2, 87654321
print "Before XCHG",13,10
print str$(var1)," var1",13,10
print str$(var2)," var2",13,10,13,10
mov esi, var1
mov edi, var2