News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Which registers are safe, control questions

Started by Alek, December 03, 2018, 11:53:12 AM

Previous topic - Next topic

Alek

So just a few general questions:

1. Lets say I have some value I want to store temporarily in a register - which register is "safe"? For instance I make to function calls, both return into eax so I need to store the value of the first return somewhere else. Perhaps this is in a loop so say ecx is also in use, etc.

2. Now lets say that this value is an array of bytes, a string. Can I just "mov" the 32-bit string into a register?

3. Do all PROCs need a prototype? For instance this:

abc PROC
push 0
ret
abc ENDP
END abc

4. Does x86 have a "jump and link"? For instance, MIPS (RISC) allows you to jump to a label, then you can jump back to where you left off - or should this be done exclusively using procedures?


Thanks again for all the help!

felipe

Quote from: Alek on December 03, 2018, 11:53:12 AM
1. Lets say I have some value I want to store temporarily in a register - which register is "safe"? For instance I make to function calls, both return into eax so I need to store the value of the first return somewhere else. Perhaps this is in a loop so say ecx is also in use, etc.

This is a too much a general question. So you should put this question into a context. i.e. if is a windows api function, etc.

Quote from: Alek on December 03, 2018, 11:53:12 AM
2. Now lets say that this value is an array of bytes, a string. Can I just "mov" the 32-bit string into a register?

If the string is really a 32 bit length string of course!

Quote from: Alek on December 03, 2018, 11:53:12 AM
3. Do all PROCs need a prototype? For instance this:

abc PROC
push 0
ret
abc ENDP
END abc

No.

Quote from: Alek on December 03, 2018, 11:53:12 AM
4. Does x86 have a "jump and link"? For instance, MIPS (RISC) allows you to jump to a label, then you can jump back to where you left off - or should this be done exclusively using procedures?
No need of procedures, but there is no "jump and link" instruction, so you can do 2 jumps.  :idea:

Alek

So in context for question 1, lets say I have 10 different operations and I want to store them each separately. Their values can require all 32 bits. What do we do?

For question 3, I have "call abc" in my "main PROC", however "The current stack frame was not found in a loaded module. Source cannot be shown for this location." I'm using AsmDude which tells me (without running the application) that "abc" is an undefined label. So how would I call this procedure?

hutch--

Alek,

Register preservation rules are called the Intel ABI (Application Binary Interface) which are split between volatile registers and non volatile registers.

Volatile registers are EAX ECX EDX
Non volatile registers are EBX ESI EDI
Stackframe and basepointer reguisters EBP ESP

The volatile registers can be modified safely but you need to know that any procedure call you make can also modify them.
Non volatile registers must be preserved if they are used in a procedure.
The EBP register can be used if it is first preserved AND you are writing a procedure that has no stack frame.
The ESP register is the stack pointer and you need to know exactly what you are doing before you try and modify it.

Return values for the normal integer registers are always in EAX.

Return values for FP and SSE use different registers.

With normal procedures which use a stack frame, you have 6 registers and EBP and ESP are used to set up the stack frame.

If you need to have more than 3 registers you must preserve the 3 non volatile registers.

myproc proc args etc ....
    push ebx
    push esi
    push edi

  ; all of your procedure code

    pop edi
    pop esi
    pop ebx

    ret <arg count x 4>
myproc endp

hutch--

Question 2

No, you must determine the ADDRESS of the string and load the ADDRESS into a register or 32 bit variable.

Question 3
NO you don't have to use a prototype but you cannot use "invoke" without one. You can use the normal PUSH/CALL notation to call the procedure manually.

Alek

Thanks for the detailed response. One more final question for tonight (if you're up for it):

So you said that I must determine the address of the string and load it into a register. Here's an example of what I specifically want to do

I have this in my code (.data section):

message db "Hello World",13,10

However I want to reuse it because Ill use this in a console procedure which always uses this address for the message parameter.

1. Is it safe to overwrite the data of "message" and continually use its address?
2. Is this approach a "good" or "bad" practice?

Thanks again to both hutch-- and Felipe.



hutch--

There are a couple of ways.

.data
TextMsg db "Howdy Folks",13,10,0  ; must have the terminating "0"
pTxt dd TextMsg
.code
etc ....

If its called from an "invoke" operator you can use the "ADDR TextMsg" notation.

If you need to load the address into a register you can use "mov eax, OFFSET TextMsg".

You can also use "lea eax, TextMsg".

hutch--

There is nothing wrong with reusing an address but its done differently so you are safe with the loaded length of any new text. If you need GLOBAL scope you allocate a buffer in the uninitialised data section.

.data?
  buffer db 260 dup (?)
.code
  etc ....


You can also use LOCAL memory if the buffer is only needed in a procedure.

  LOCAL TxtBuff[260]:BYTE
  LOCAL ptxt :DWORD

    lea eax, TxtBuff
    mov ptxt, eax


Alek

Fantastic, Ill try using everything you mentioned.

Alek

Alright, here's a better idea of what I'm trying to do.

.data
lpBuffer2 db 256 dup (?)

.code
;mov dword ptr lpBuffer, "Hello World!"
mov dword ptr lpBuffer2+8, "dlro"
mov dword ptr lpBuffer2 + 4, "W ol"
mov dword ptr lpBuffer2, "leH"   
mov nNumberOfCharsToWrite, 12
call print_message


Assume the print_message works, which it does. Is there any way to move over the characters in one chunk at a time, like I have in the commented section?

hutch--

It looks like you are trying to load string memory from registers with the reverse order characters. It can be done but its by no means the only way to load string data. String data is normally written left to right and you would normally stream it to the start address 1 characters at a time. You see a bit of stuff done like this with the CPUID instruction when you want processor ID data but that is hardware based, not code.

Try and tell us what you are doing and there may be a better way to do it.

Alek

Well I want to try and call strings like I normally would in a higher level language.


For instance right now the way most people write strings in assembly (c++ pseudo-code):
std::string a = "hello world";
std::cout << a << std::endl;


The way I want to write my string output in assembly code:
std::cout << "hello world" << std:endl;


As you can see, the field "a" would be defined in the data section. In the second example, its defined locally. Why the two versions? It gives more flexibility; perhaps there's some string I want to create that cannot be defined ahead of time.

Example (again):
1. Program asks for username
2. Program accepts username and combines it with string
3. Program can now output "Alek, hello world".

Alternatively you would output each part separately in different calls - but from what I know making those high level Windows API calls are slow and it would be faster to combine the string ahead of time. Of course this is only one reason, not counting "clarity" as being a preference (to me it would make more sense coming from a higher level like C/C++).

hutch--

Let me see, it sounds like you want to store text in an assembler module and call it from what looks like C++. With either ASCII or UNICODE the fastest way is to place the string data in the initialised data section and set pointers to each string.

  .data
    txt1 db "This is a test",0  ; the text
    ptxt1 dq txt1               ; the pointer to it

If you have a large number of text items you could use a QWORD array in allocated memory (Heap or Global Alloc). With either you would just pass the address. ASCII data is byte aligned, unicode is at least 2 byte aligned.

I had a look at your page and it would be simplified with the install64.zip file. The problem is I must keep adding to the project so it will keep changing.

RE: The project name, It would require a complete rebuild to be able to use MASM64 which I don't have the life and time for so it would make more sense to call it "MASM32 64 bit version".

Vortex

Hi Alek,

You can use the Masm32 library functions to receive keyboard input and write it to the console :

include     \masm32\include\masm32rt.inc

BUFFER_SIZE = 128

.data

string1     db 'Please type your name : ',13,10,0

string2     db ', hello world.',0

.data?

buffer      db BUFFER_SIZE dup(?)

.code

start:

    invoke  StdOut,ADDR string1

    invoke  StdIn,ADDR buffer,BUFFER_SIZE

    invoke  szCatStr,ADDR buffer,ADDR string2

    invoke  StdOut,ADDR buffer

    invoke  ExitProcess,0

END start


Another option is the use the functions from the MS C run-time library :

include     \masm32\include\masm32rt.inc

BUFFER_SIZE = 128

.data

format      db '%s',0
string1     db 'Please type your name : ',13,10,0
string2     db '%s, hello world.',0

.data?

buffer      db BUFFER_SIZE dup(?)

.code

start:

    invoke  crt_printf,ADDR string1

    invoke  crt_scanf,ADDR format,ADDR buffer

    invoke  crt_printf,ADDR string2,ADDR buffer

    invoke  ExitProcess,0

END start


hutch--

Hi Alex,

I don't know if this answers your question properly but give it a try. It builds OK with the 64 bit version.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    include \masm32\include64\masm64rt.inc

  .data
    ; titl db "MessageBox",0
    ; pttl dq titl

;     ifmt1 db "Array:",13,10     ; control string
;           db "mas3[0] = %d"
;           db " mas3[8] = %d",0

    ; ifmt1 db "Array:",13,10,"mas3[0] = %d"," mas3[8] = %d",0

    ; pcst dq ifmt1               ; pointer to control string

  .code

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

entry_point proc

    ; invoke MessageBox,0,pcst,"MessageBox",MB_OK

    rcall MessageBox,0,chr$("Array:",13,10,"mas3[0] = %d"," mas3[8] = %d"),"MessageBox",MB_OK

    invoke ExitProcess,0

entry_point endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

  end


This is the batch file to build it.

@echo off

set appname=alex

if exist %appname%.obj del %appname%.obj
if exist %appname%.exe del %appname%.exe

\masm32\bin64\ml64.exe /c %appname%.asm

\masm32\bin64\polink.exe /SUBSYSTEM:WINDOWS /MACHINE:X64 /ENTRY:entry_point /nologo /LARGEADDRESSAWARE %appname%.obj

dir %appname%.*

pause