flat assembler
Message board for the users of flat assembler.
Index
> Windows > Learning Win32 programming Goto page Previous 1, 2, 3 Next |
Author |
|
AsmGuru62 25 Apr 2012, 19:26
So, AL is also a DB -- why is it crashing?
On which line is it crashing exactly? |
|||
25 Apr 2012, 19:26 |
|
bzdashek 25 Apr 2012, 19:52
This doesn't crash, Inagawa:
Code: format PE console entry start include 'win32a.inc' start: call !GetChar movzx eax,[Look] cinvoke putchar,eax cinvoke exit,0 Look db ? proc !GetChar uses edx cinvoke getchar mov [Look], al ret endp data import library msvc,'MSVCRT.DLL' import msvc,\ getchar,'getchar',\ putchar,'putchar',\ exit,'exit' end data |
|||
25 Apr 2012, 19:52 |
|
Inagawa 26 Apr 2012, 12:06
Ah, thanks. The problem was on my side, a typo in '.data' section. It's all good for now.
|
|||
26 Apr 2012, 12:06 |
|
typedef 26 Apr 2012, 21:11
Inagawa wrote: Ah, thanks. The problem was on my side, a typo in '.data' section. It's all good for now. doesn't matter what name you give to a section as long as it's 8 bytes and has appropriate page attributes (i.e: Read / Write / Execute |
|||
26 Apr 2012, 21:11 |
|
Inagawa 27 Apr 2012, 11:12
Yes, I have no idea how it happened, but my .data section was marked as "readable executable", that's why it crashed, there was no way to write there.
Anyway, guys. Could you give me an idea how to approach variadic functions? I was thinking something about the first argument marking "how many" DWORD arguments there are. There has to be a better way. Or are compilers doing it like this and then implicitly passing the number of arguments to the function? Something like this. Code: proc !Emit uses ecx ebx, Count:DWORD, Output mov ebx, [Output] mov ecx, [Count] .OutputLoop: push ecx invoke lstrlenA, ebx invoke WriteFile, [_fileHandle], ebx, eax, _bytesWritten, 0 pop ecx add ebx, 4 loop .OutputLoop ret endp _bytesWritten dd ? |
|||
27 Apr 2012, 11:12 |
|
revolution 27 Apr 2012, 11:40
You might want to make the varadic function use the C calling standard and allow the caller to release the stack.
Code: ccall printf, arg1, arg2, arg3 proc printf c uses regs, args lea ebx,[args] ;code endp |
|||
27 Apr 2012, 11:40 |
|
Inagawa 27 Apr 2012, 11:44
I don't get it. The three dots are part of the code? Like the variable argument list from C? Perhaps an example might get the idea across, please?
Edit: I see, the dots got me confused. I'll look into this Edit2: I'm going back to doing it by hand, the procedures are nothing but a source of confusing problems. |
|||
27 Apr 2012, 11:44 |
|
AsmGuru62 27 Apr 2012, 15:51
@Inagawa:
Why do it by hand? As there exists a nice macro ccall, which does exactly that -- pass ANY # of parameters onto stack AND automatically release the stack after the call. This macro works right out of the box from FASM package -- no need to do any additional effort. Notice how in the following code I am not using 'proc' to declare my functions -- just using a label with no dot in front: Code: Foo: ; ; At this point your parameters begins at address [ESP+4], ; because [ESP] points to a return address on stack ; (from where you called). ; ret ... ccall Foo, 1, 2, 3, 4 ; Pass ANY # of parameters here ... mov eax, <something> ccall BlackBox, a, b, c ; ; EAX is preserved by BlackBox! ; ... ; ; This function will not damage ANY registers ; BlackBox: pusha lea ebp, [esp + 36] ; 36 = 8 registers pushed by PUSHA + ret. address ; ; Now 1st parameter is at [EBP] ; 2nd is at [EBP+4] ; 3rd is at [EBP+8] and so on ; popa ret ; ; What if you need to return some register back to caller, like EAX or other? ; No problem! All you need, is to store the return value into its place into area ; taken by PUSHA and it will be loaded back by POPA before RET. ; Get_EAX_Back: pusha lea ebp, [esp + 36] ; ... do all kind of calculations here ... ; ; Store ESI into EAX place on stack ; Why 7*4? Because EAX pushed 1st by PUSHA ; (read CPU manual -- the order is defined there) ; mov [esp + 7*4], esi popa ; This will load EAX with the value of ESI before returning ret ; ; To avoid the offsets used with EBP to access parameters - just use ; a 'virtual' statement for your parameters: ; virtual at 0 PARAMS: .P1 dd ? .P2 dd ? .P3 dd ? ; ; ... and so on ... ; end virtual ; ; then inside a function - you can just use this: ; mov ecx, [ebp + PARAMS.P5] ; ; instead of fuguring out the offset to parameter #5 ; |
|||
27 Apr 2012, 15:51 |
|
Inagawa 28 Apr 2012, 11:00
This is immensely helpful! Let me just ask a couple of follow up questions to make it clear for me.
At the end of your Get_EAX_Back function, could I do it like that? Code: popa mov eax, esi ; This will load EAX with the value of ESI before returning ret Also, what makes you push all the registers? I thought EAX, ECX and EDX are scratch registers and saving them all is just a waste of clock cycles - am I wrong? I still don't understand how do functions know how many parameters are on the stack specifically for them. How do functions know how far to reach out to the stack without reading data that wasn't pushed for them? I need to study this, I'll be here later. And thanks a lot |
|||
28 Apr 2012, 11:00 |
|
AsmGuru62 28 Apr 2012, 11:29
Code: pusha ... popa ; This line will restore old ESI from stack and ; damage the ESI you calculated to return mov eax, esi ; old ESI returned instead of the new one. ret Saving all registers can be very useful to write code, because when you write code, you know that most of your functions do not damage any registers, so if you hold some value across a few calls -- this value is still there, in the register, so it may be reused again, for some other purpose. However, if some function is used which actually returns some value, then of course, that register is destroyed. I see, that you trying to optimize your code early -- a good thing, but not really making a difference. The 'real' optimization is picking the proper algorithm for your task. Also, optimizing makes sense if you process a lot of information, like reading and processing 100Mb file or some very large nested loop(s). Or calculating some formula millions of times per second. In extreme cases -- so to speak. Also, without measuring the speed of your code, you can't really be sure that your optimization works. So, in case you really want to get results from the optimization of the piec of code -- you need to measure the amount of relative time this piece uses. For that purpose, there is an API: QueryPerformanceCounter(). It must be called before the piece of code to be measured and after it and then subtract the results. You need to run that measurement a few times, then take an average of your measurements. Then you make some improvements to the code and measure it again. In most cases, the result of improvement is not really visible. However, if the improvement is an algorithm change -- THEN you will see some results! Now, you were asking how the function 'knows'? But you are the one writing that function! You can pass a count of values you have as parameters as a first parameter and then use it inside the function to access other parameters. The question is what is your function doing with variable number of parameters? What exactly do you need it for? I suspect that you have an improper code design somewhere. I write code for a lot of years and I never needed functions like that. Well, there are printf kind of functions, but they are all written already! Or, maybe you need to pass an array of values to the function and the count is not known. For these cases you pass TWO parameters: - an address of the first value in the array (the rest will follow the 1st) - a count of values in the array. You see: two parameters is enough to pass an array to any function. |
|||
28 Apr 2012, 11:29 |
|
Inagawa 28 Apr 2012, 14:25
I will need a lot of time to digest all this, thanks so much for the extensive info.
Can I make virtual local to the function? Just like the .P1 is local to PARAMS, can PARAMS be local to a function? Edit: I took what you told me earlier and used it. It seems to work perfectly. Am I using everything okay? Code: ;================================================================ ;=== EMIT.INC =================================================== ;================================================================ ; Description: Emits a line of assembly into a txt file. ; ; Arguments: Count, ... ;================================================================ !Emit: ;=== PROLOGUE ================================== push ebx ebp mov ebp, esp ;=== WRITE TO FILE ============================= mov ecx, [!Emit.Params.Count] lea ebx, [!Emit.Params.Strings] .Output: push ecx ; ; Get the string length and output ; invoke lstrlenA, DWORD[ebx] invoke WriteFile, [_fileHandle], DWORD[ebx], eax, _bytesWritten, 0 add ebx, 4 pop ecx loop .Output ;=== EPILOGUE ================================== ; ; Restore the stackframe and return success ; pop ebp ebx mov ecx, 1 ret ;=== DATA ======================================================= ; ; 2x4 (EBX EBP) + 4 (Return Address) ; virtual at ebp+12 !Emit.Params: .Count dd ? .Strings dd ? end virtual _bytesWritten dd ? |
|||
28 Apr 2012, 14:25 |
|
Inagawa 29 Apr 2012, 15:12
I also discovered that my !Emit cannot output numbers. I need to create a function that will translate a number into ascii. I created this:
Code: ;============================================================================= ;=== NUMBER TO LETTER.INC ==================================================== ;============================================================================= ; Description: Less-than-elegant function to convert raw numbers into ASCII. ; ; Arguments: Number ;============================================================================= !NumberToLetter: ;=== PROLOGUE ================================== push ebp ebx esi edi mov ebp, esp ;=== GET THE NUMBER SIZE ======================= mov eax, [!NumberToLetter.Params.Number] cmp eax, 10 jle .NumberBelow_10 cmp eax, 100 jle .NumberBelow_100 cmp eax, 1000 jle .NumberBelow_1_000 cmp eax, 10000 jle .NumberBelow_10_000 cmp eax, 100000 jle .NumberBelow_100_000 cmp eax, 1000000 jle .NumberBelow_1_000_000 cmp eax, 10000000 jle .NumberBelow_10_000_000 cmp eax, 100000000 jle .NumberBelow_100_000_000 cmp eax, 1000000000 jle .NumberBelow_1_000_000_000 ;=== INITIALIZE THE DIVISOR ==================== .NumberBelow_10: mov ecx, 10 mov esi, 1 jmp .Divide .NumberBelow_100: mov ecx, 100 mov esi, 2 jmp .Divide .NumberBelow_1_000: mov ecx, 1000 mov esi, 3 jmp .Divide .NumberBelow_10_000: mov ecx, 10000 mov esi, 4 jmp .Divide .NumberBelow_100_000: mov ecx, 100000 mov esi, 5 jmp .Divide .NumberBelow_1_000_000: mov ecx, 1000000 mov esi, 6 jmp .Divide .NumberBelow_10_000_000: mov ecx, 10000000 mov esi, 7 jmp .Divide .NumberBelow_100_000_000: mov ecx, 100000000 mov esi, 8 jmp .Divide .NumberBelow_1_000_000_000: mov ecx, 1000000000 mov esi, 9 jmp .Divide ;=== DIVIDE ==================================== .Divide: mov edx, [!NumberToLetter.Params.Number] .DivisionLoop: ; ; Move the Number inside EAX ; Sign-extend EAX ; Divide EDX:EAX by ECX ; mov eax, edx cdq idiv ecx ; ; Quickstore EDX and EAX ; push edx push eax ; ; Move the main divisor into EAX ; Move a new, temporary divisor into ECX ; Sign-extend EAX ; Divide the main divisor by the temporary one ; Load the new, smaller divisor back into ECX ; mov eax, ecx mov ecx, 10 cdq idiv ecx mov ecx, eax ; ; Restore EAX and EDX ; pop eax pop edx ; ; Temporary output ; push eax ecx edx cinvoke printf, <'Single numbers: %i', 10>, eax pop edx ecx eax ; ; Exit prematurely if ECX is 0. (Cannot divide by 0) ; cmp ecx, 0 je .End dec esi cmp esi, 0 jmp .DivisionLoop .End: ;=== EPILOGUE ================================== pop edi esi ebx ebp ret ;=== DATA ==================================================================== ; ; 4x4 (EBP EBX ESI EDI) + 4 (return address) = 20 ; virtual at ebp+20 !NumberToLetter.Params: .Number dd ? end virtual It's really ugly, though. If someone wants to look at it and point out faults in it, I'd be most happy. |
|||
29 Apr 2012, 15:12 |
|
AsmGuru62 29 Apr 2012, 22:48
Here is a couple of functions:
They will convert the EAX into a string. 1st function will assume EAX is unsigned value. 2nd function will assume EAX is signed value. Code: Lib_UInt32_to_ASCII: ; --------------------------------------------------------------------------- ; INPUT: ; EAX = unsigned value to convert ; EDI = ASCII buffer ; --------------------------------------------------------------------------- pusha push 10 pop ecx ; ECX = 10 (EAX will be divided by 10) xor ebx, ebx ; EBX = 0 (counter of digits to store) .divide_by_10: xor edx, edx div ecx ; 64bit value in EDX:EAX divided by 10 add dl, '0' ; DL is now a digit in range ('0'..'9') push edx ; Put it into stack inc ebx ; Count how many digits are put on stack test eax, eax ; Is there more to divide? jnz .divide_by_10 ; ; At this point all the digits stored on stack ; .store_to_buffer: pop eax ; AL = next digit stosb ; Store it into buffer at EDI and increment EDI sub ebx, 1 ; Is there more digits on stack jnz .store_to_buffer mov [edi], bl ; Terminate buffer with null character popa ret Lib_Int32_to_ASCII: ; --------------------------------------------------------------------------- ; INPUT: ; EAX = signed value to convert ; EDI = ASCII buffer ; --------------------------------------------------------------------------- ; Now, before the output we'll need to test if the value in EAX has a sign. ; If it has -- value is made into unsigned and then Lib_UInt32_to_ASCII is ; used to print it. ; --------------------------------------------------------------------------- pusha add eax, 0 jns .print_me ; ; Value in EAX is negative ; neg eax mov byte [edi], '-' inc edi .print_me: call Lib_UInt32_to_ASCII popa ret I tested them in debugger with some simple code: Code: sBuffer db '*****************' ... mov edi, sBuffer mov eax, 0FFFFFFFFh call Lib_UInt32_to_ASCII call Lib_Int32_to_ASCII xor eax, eax call Lib_UInt32_to_ASCII mov eax, 6400 imul eax, 100 call Lib_UInt32_to_ASCII neg eax call Lib_Int32_to_ASCII |
|||
29 Apr 2012, 22:48 |
|
Inagawa 30 Apr 2012, 18:53
You have been programming for quite a while, haven't you?
What do these lines do? Code: add dl, '0' ; DL is now a digit in range ('0'..'9') ;==== test eax, eax ; Is there more to divide? Also, why do you initialize ECX by pushing the 10 on stack and then popping it into ECX? Is that a better practice? Edit: Oh, I understand the "add dl, '0'"! But I'd never think of that, to be honest. Edit2: I found out that the test checks if eax is zero. Why do it like this? Is this better than doing it like cmp eax, 0 (I have heard somewhere that code with lots of explicitly declared 0s is bad) Last edited by Inagawa on 30 Apr 2012, 19:28; edited 1 time in total |
|||
30 Apr 2012, 18:53 |
|
AsmGuru62 30 Apr 2012, 19:28
Well, this:
Code:
push 10
pop ecx
will result in smaller code (3 bytes). Against this: Code: mov ecx, 10 ; 5 bytes of code However, the MOV is of course, faster than TWO instructions. You need to choose the one for the case you working on. TEST is recommended instruction to check the register for zero. Code: test eax, eax jz .Reg_EAX_is_Zero ... test ecx, ecx jnz .Reg_ECX_is_not_zero TEST with zero may be slower than TEST with same register. The opcodes where both operands are registers are preferred against the ones where one of the operands is in memory. In this case zero is called immediate operand, however, it is still in memory. |
|||
30 Apr 2012, 19:28 |
|
typedef 30 Apr 2012, 20:17
TEST is also good if you are going to use the result otherwise one could also use AND which modifies the operand.
|
|||
30 Apr 2012, 20:17 |
|
Inagawa 07 May 2012, 09:47
I have returned back to Win32 programming and there's something basic I haven't really understood. It's about subtracting from ESP when you call a procedure. All the books say it's for the "automatic variables", so does that mean that I have to subtract 4 for every push I do on the stack?
Let's say I have a procedure, Code: push ebp mov ebp, esp sub esp, 16 push eax push eax push eax push eax pop eax pop eax pop eax pop eax add esp, 16 pop ebp Do I have it right? I simply have to count all the PUSHes and subtract their (count*4) from ESP? If I really had to guess, I'd say it's in case that processor starts another timeslice, it doesn't trample over my auto variables, since the stack top is shifted. Is it something like that? |
|||
07 May 2012, 09:47 |
|
AsmGuru62 07 May 2012, 14:29
The ESP is used to reserve space on stack for local (some call it automatic) variables.
If you do not have the locals in a procedure - you do not need to do anything with ESP. All you need to ensure is that when your code 'meets' the RET instruction - the return address is on the top of the stack. That means you need to not count PUSH/POP, but you can balance the stack some other way, like this: Code: push eax push eax push eax push eax ... add esp, 4*4 ; <-- 4 DWORDs were PUSH-ed ret There is no connection to a CPU time slices. The task switch will not disturb any of your data. However, in a multithreaded code - you DO need to protect the data, which is a READ/WRITE data for multiple threads. If you have read only data - and it is read (but not written to) by multiple threads - you do not need to protect such data. CPU will properly read it from multiple threads. EBP is needed for access to the parameters passed to a function and local variables. FASM has a macro 'local' - this macro fully automates the work with local variables in a procedure, but then you need to also use the 'proc' macro to define your functions. These macros work together, I believe. Last edited by AsmGuru62 on 09 May 2012, 16:46; edited 1 time in total |
|||
07 May 2012, 14:29 |
|
Inagawa 07 May 2012, 14:41
Thanks for the concise answer, the term "auto variables" confused me, it's not the best choice of words in an assembly environment.
|
|||
07 May 2012, 14:41 |
|
Goto page Previous 1, 2, 3 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.