Greetings:
I hope this is the proper place for my source code questions.
I wrote the following program so I could get a better understanding of what code is being generated by EBasic/NASM.
OPENCONSOLE
DEF Obj1, Obj2, myResult as INT 'define three integer variables
myResult = myProc(100) 'pass parameter â€ËÅ"100’ to myProc
PRINT "The results!"
PRINT "Obj1 value is: ", Obj1
PRINT "Obj2 value is: ", Obj2
PRINT "myResult value: ", myResult
DO:UNTIL INKEY$<>""
CLOSECONSOLE
END
SUB myProc(number as INT), INT
DEF c as INT
c=10
_asm
mov eax, dword [ebp-4] ;get local variable 'c' off the stack
mov dword [$Obj1], eax ;store value of 'c' in Obj1 (Obj1=10 now)
mov eax, dword [ebp+8] ;get parameter off the stack (number=100)
mov dword [$Obj2], eax ;store parameter value in Obj2 (Obj2=100 now)
_endasm
return c ;return the value of â€ËÅ"c’ to the caller (c=10)
ENDSUB
Compiling the above code generated the following EBasic â€Ã...“Aâ€Ã, file:
001 segment .text use32 align=16
002 extern _ibstrcpy
003 extern _ibstrlen
004 extern _ibstrcat
005 extern _ib_finish
006 extern _i64mul
007 extern _i64div
008 extern _ui64div
009 extern _i64rem
010 extern _ui64rem
011 extern _sys_double
012 extern OPENCONSOLE
013 extern CLOSECONSOLE
014 extern __imp_lstrcmpA
015 extern INKEY$
016 extern _ibprint
017 extern FreeHeap
018 %define _@@myProc_ 4
019 global _ib_main
020 _ib_main:
021 call dword $OPENCONSOLE ;open in console mode
022 mov eax, $myResult ;put address of myResult into eax register
023 push eax ;push address on the stack
024 mov eax,100 ;put parameter to be passed into eax register
025 push eax ;push parameter on the stack
026 call dword $myProc ;call myProc SUBROUTINE
027 mov edx,eax ;results of myProc returned in eax
028 pop esi ;NOT SURE WHAT ADDRESS IS BEING POPPED
029 mov [esi],edx ;IN ANY EVENT, STORE THE RESULTS THERE
030 push dword 0x0a ;push 10 decimal on the stack
031 push dword 1 ;push 1 on the stack
032 push dword 0x0d ;push 13 decimal on the stack
033 push dword 1 ;push 1 on the stack
034 mov eax,STR00000 ;put pointer to â€Ã...“The results!â€Ã, string into eax
035 push eax ;push pointer on the stack
036 push dword 0 ;push 0 on the stack
037 push dword 3 ;push 3 on the stack
038 call _ibprint ;print â€Ã...“The results!â€Ã, string to the console
039 add esp,28 ;make room on the stack for 7 parameters
040 push dword 0x0a
041 push dword 1
042 push dword 0x0d
043 push dword 1
044 mov eax, $Obj1 ;put pointer to Obj1 into eax register
045 push dword [eax] ;push value of Obj1 (10) on the stack
046 push dword 2
047 mov eax,STR00001 ;put pointer to â€Ã...“Obj1 value is: â€Ã, string into eax
048 push eax ;push string pointer on the stack
049 push dword 0
050 push dword 4
051 call _ibprint ;print â€Ã...“Obj1 value is: 10â€Ã, to the console
080 L0790000:
081 mov eax,STR00004
082 push eax
083 xor ebx,ebx
084 mov edx,ebx
085 push edx
086 call dword $INKEY$
087 mov esi,eax
088 pop eax
089 mov ebx,esi
090 mov esi,ebx
091 mov edi,eax
092 push eax
093 push ebx
094 push edi
095 push esi
096 call [__imp_lstrcmpA]
097 xor edx,edx
098 cmp eax,0
099 setne dl
100 push edx
101 push esi
102 Call FreeHeap
103 pop edx
104 pop ebx
105 pop eax
106 mov eax,edx
107 cmp eax,dword 0
108 jz near dword L00000
109 call dword $CLOSECONSOLE
110 jmp _ib_finish
111 ;subroutine myProc begin
112 jmp L00001
113 align 16
114 $myProc:
115 .bf
116 push ebp
117 mov ebp,esp
118 sub esp,_@@myProc_ ;_@@myProc_=4 making room for local variable
119 push ebx ;EBasic requires ebx, edi, esi to be preserved
120 push edi ;so push them on the stack
121 push esi
122 lea eax,[ebp-4] ;get address of the local variable on the stack
123 mov [eax],dword 10 ;put the value â€ËÅ"10’ into the local variable â€ËÅ"c’
124
125 mov eax, dword [ebp-4] ;get local variable 'c'
126 mov &Obj1, eax ;store value of 'c' in Obj1
127 mov eax, dword [ebp+8] ;get parameter off stack (â€Ã...“number=100â€Ã,Â)
128 mov &Obj2, eax ;store parameter value (100) in Obj2
129 lea eax,[ebp-4] ;get address of â€ËÅ"c’ on the stack
130 mov edx,[eax] ;get value of â€ËÅ"c’ into edx
131 mov eax,edx ;set up to return the value of â€ËÅ"c’ to caller
132 pop esi ;restore esi, edi, ebx registers
133 pop edx ;as required by EBasic
134 pop ebx
135 mov esp,ebp ;restore top-of-the-stack pointer
136 pop ebp ;restore stack base pointer
137 ret 4 ;clear stack of the passed parameter
138 .ef
139 L00001:
140 jmp _ib_finish
141
142 segment .data use32 align=4
143 align 4
144 $STR00000 db "The results!",0
145 align 4
146 $STR00001 db "Obj1 value is: ",0
147 align 4
148 $STR00002 db "Obj2 value is: ",0
149 align 4
150 $STR00003 db "myResult value: ",0
151 align 4
152 $STR00004 db "",0
153 segment .bss use32 align=4
154 alignb 4
155 $myResult resd 1
156 alignb 4
157 $Obj2 resd 1
158 alignb 4
159 $Obj1 resd 1
Looking at the generated code above raises several questions for me. I searched the forum for some answers, but didn’t find what I needed. Perhaps a more intelligent and better looking person can assist me.
First question: I can’t figure out what address is being popped off the stack in line 028. I have drawn outlines of the stack and can’t keep track of the state of the stack at the time the pointer address is being popped into esi. It’s driving me nuts.
Second question: I can see in lines 030â€ââ,¬Å"037 and again in lines 040-050 parameters are being passed onto the stack in preparation for a call to _ibprint. I purchased the EBasic source file package, but can’t find any reference to _ibprint in any of the files. I must be overlooking something simple here.
Where do I find the source code for external procedures like _ibstrcpy, _ibprint, _ib_finish, OPENCONSOLE, etc. in the source files-or elsewhere? I’d like to understand the nature of the parameters being passed on the stack to these external procedures. Is there a listing/reference or some code I can glean in order to see what parameters are required to be passed to the respective external procedures?
Third question: I see in line 096 there is a call to the external [__imp_lstrcmpA] procedure. Where do I locate _imp_lstrcmpA? This doesn’t seem to be an actual API procedure per se; at least I can’t find it in the API listing or in any of the source files. Could someone with more experience and sophistication point me in the right direction on this one? I’m not sure what it represents other than some kind of string comparison procedure/structure.
Fourth question: I can't figure out why in lines 040-050 we are passing 9 parameters onto the stack, but in line 039 the compiler is only reserving enough stack space for 7 parameters to be passed.
Final question: Line 110 contains a jump to a _ib_finish procedure. However, two lines later in line 112 there is a jmp L00001 instruction that jumps to line 139, which contains the L00001 label, which leads to another jmp _ib_finish instruction. I can’t figure out why lines 111-112 have been put into the code. My guess is that this is just how the compiler does things preceding a procedure.
Any hints from smarter folks than myself would be very much appreciated. Thanks in advance.
Logman
Quote from: Logman on October 29, 2009, 08:40:07 AM
First question: I can’t figure out what address is being popped off the stack in line 028.
At the first glance:
"$myResult" it pushed at 23
Quote
Second question: I can see in lines 030–037 and again in lines 040-050 parameters are being passed onto the stack in preparation for a call to _ibprint. I purchased the EBasic source file package, but can’t find any reference to _ibprint in any of the files. I must be overlooking something simple here.
"_ibprint" situated in the corresponding "conmain.o","winmain.o" or "dllmain.o"
My answer is not 100% here I am just writing it without to checking but since print can be arbitrary length the first param has to be the parameter-count.
The second is the type bit 16 determines direct in-line or variable.
The third is what to print
The fourth is type (char) for return.
The fifth is return (13)
The sixt is type (char) for newline
The seventh is newline (10)
If exist eight is the Hwnd.
Quote
Where do I find the source code for external procedures like _ibstrcpy, _ibprint, _ib_finish, OPENCONSOLE, etc. in the source files-or elsewhere?
I don't know.
but OPENCONSOLE must be something like this in MASM syntax:
OPENCONSOLE proc near public
cmp _exe_type, 0
jz @F
jmp dword ptr ds:__imp_AllocConsole
@@: retn
OPENCONSOLE endp
_ib_finish is not a sub it is just a jump to exit like if your "conmain.o" would look like this: -without _ibprint-
; *** Conmain ***
segment .text use32 align=4
global heap_stack
global heap_limit
global heap_stack_ptr
global heapptr
global _main
global _exe_type
global _ib_finish
global u_exitcode
global pOnExit
;--------- OPTIONAL includes ------------
extern CreateHeap
;extern CreateLowFragHeap
;-------------------------------------------------------
extern _ib_main
extern DestroyHeap
extern __imp_GetModuleHandleA
extern __imp_ExitProcess
; ===========================
_main: finit
push ebp
mov ebp, esp
;------- Component ---------
call CreateHeap
;call CreateLowFragHeap
;-------------------------------------------------------
test eax, eax
jz short Exit
pusha
mov [_stack_save], esp
;-------------------------------------------------------
jmp _ib_main
; ===========================
; ===========================
_ib_finish:
mov esp, [_stack_save]
popa
call DestroyHeap
Exit: leave
push byte 0
call [__imp_ExitProcess]
; ===========================
segment .data use32 align=4
$heap_stack_ptr dd 0
heap_limit dd 1000
_exe_type db 0
; ===========================
segment .bss use32 align=4
u_exitcode resd 1
$_stack_save resd 1
$pOnExit resd 1
$heapptr resd 1
$heap_stack resb 4000
_ibstrcpy ist just a string copy destination, source.
Quote
Third question: I see in line 096 there is a call to the external [__imp_lstrcmpA] procedure. Where do I locate _imp_lstrcmpA?
http://msdn.microsoft.com/en-us/library/ms647488(VS.85).aspx (http://msdn.microsoft.com/en-us/library/ms647488(VS.85).aspx)
Quote
Fourth question: I can't figure out why in lines 040-050 we are passing 9 parameters onto the stack, but in line 039 the compiler is only reserving enough stack space for 7 parameters to be passed.
There is no reservation on stack. There is a balancing after the call.
On line 52 get's the stack balanced for the call on line 51.
Quote
Final question: Line 110 contains a jump to a _ib_finish procedure....
These chains of jumps are just residual codes should be cleaned up.
It looks like the compiler generates code for the worst - most complicated - scenario and if your code turns out more simple it just leave the trash there.
Lot's of questions what I get payed for that ? ;D
Regards,
Ficko
Ficko:
Thanks for the comments. I know my thread was kind of long-winded, but once I pick up the scent of the trail, I can usually take it from there--I just need a hint or two to get going from really smart folks like you.
You have come through once again Ficko. Why do I get the feeling you've had a lot of experience in the past working at the assembly level.
Your comments have pointed me in the right direction. I'm going to use them to dig deeper into the inner workings of EBasic/NASM.
Thank you for your time-I really appreciate it. ;D
Logman
Greetings again:
Thanks to Ficko, I can now correctly comment the code in my previous thread for anyone interested in the inner workings of how EBASIC writes to the console screen.
001 segment .text use32 align=16
002 extern _ibstrcpy ;external procedures found in winmain, dllmain, crtdll 'o' files
003 extern _ibstrlen
004 extern _ibstrcat
005 extern _ib_finish
006 extern _i64mul
007 extern _i64div
008 extern _ui64div
009 extern _i64rem
010 extern _ui64rem
011 extern _sys_double
012 extern OPENCONSOLE
013 extern CLOSECONSOLE
014 extern __imp_lstrcmpA
015 extern INKEY$
016 extern _ibprint
017 extern FreeHeap
018 %define _@@myProc_ 4
019 global _ib_main
020 _ib_main:
021 call dword $OPENCONSOLE ;open in console mode
022 mov eax, $myResult ;put address of myResult into eax register
023 push eax ;push address on the stack
024 mov eax,100 ;put parameter to be passed into eax register
025 push eax ;push parameter on the stack
026 call dword $myProc ;call myProc
027 mov edx,eax ;results of myProc returned in eax
028 pop esi ;get memory location of myResult off the stack
029 mov [esi],edx ;store the results in myResult variable location
030 push dword 0x0a ;10=LF line feed character
031 push dword 1 ;1=CHAR type; tells _ibprint procedure that LF represents a CHAR type
032 push dword 0x0d ;13=CR carriage return character
033 push dword 1 ;1=CHAR type; tells _ibprint procedure that CR represents a CHAR type
034 mov eax,STR0000 ;put pointer to â€Ã...“The results!â€Ã, string into eax
035 push eax ;push pointer on the stack
036 push dword 0 ;0=STRING type; tells _ibprint procedure that STR00000 represents a STRING type
037 push dword 3 ;3=number of parameters to print to console screen, i.e.: STR00000 + CR + LF
038 call _ibprint ;print â€Ã...“The results!â€Ã, string to the console by calling _ibprint in winmain
039 add esp,28 ;balance the stack by removing the preceding 7 parameters
040 push dword 0x0a ;LF
041 push dword 1
042 push dword 0x0d ;CR
043 push dword 1
044 mov eax, $Obj1 ;put pointer to Obj1 into eax register
045 push dword [eax] ;push value of Obj1 (10) on the stack
046 push dword 2 ;2=INT type; tells _ibprint procedure that Obj1 represents an INT type
047 mov eax,STR00001 ;put pointer to â€Ã...“Obj1 value is: â€Ã, string into eax
048 push eax ;push string pointer on the stack
049 push dword 0 ;0=STRING type; tells _ibprint procedure that STR00001 represents a STRING type
050 push dword 4 ;4=number of parameters to print to console screen, i.e.: STR00001 + Obj1 + CR + LF
051 call _ibprint ;print â€Ã...“Obj1 value is: 10â€Ã, to the console by calling _ibprint in winmain
080 L0790000:
081 mov eax,STR00004
082 push eax
083 xor ebx,ebx
084 mov edx,ebx
085 push edx
086 call dword $INKEY$
087 mov esi,eax
088 pop eax
089 mov ebx,esi
090 mov esi,ebx
091 mov edi,eax
092 push eax
093 push ebx
094 push edi
095 push esi
096 call [__imp_lstrcmpA] ;call to winAPI
097 xor edx,edx
098 cmp eax,0
099 setne dl
100 push edx
101 push esi
102 Call FreeHeap
103 pop edx
104 pop ebx
105 pop eax
106 mov eax,edx
107 cmp eax,dword 0
108 jz near dword L00000
109 call dword $CLOSECONSOLE
110 jmp _ib_finish ;procedure in winmain
111 ;subroutine myProc begin
112 jmp L00001
113 align 16
114 $myProc:
115 .bf
116 push ebp
117 mov ebp,esp
118 sub esp,_@@myProc_ ;_@@myProc_=4; makes room for local variable
119 push ebx ;EBasic requires ebx, edi, esi registers to be preserved
120 push edi ;so push them on the stack
121 push esi
122 lea eax,[ebp-4] ;get address of the local variable 'c' on the stack
123 mov [eax],dword 10 ;put the value â€ËÅ"10’ into the local variable â€ËÅ"c’; this is how 10 gets assigned to 'c'; i.e. c=10
124
125 mov eax, dword [ebp-4] ;get local variable 'c'
126 mov &Obj1, eax ;store value of 'c' in Obj1
127 mov eax, dword [ebp+8] ;get parameter off stack (â€Ã...“number=100â€Ã,Â)
128 mov &Obj2, eax ;store parameter value (100) in Obj2
129 lea eax,[ebp-4] ;get address of â€ËÅ"c’ on the stack
130 mov edx,[eax] ;get value of â€ËÅ"c’ into edx
131 mov eax,edx ;set up to return the value of â€ËÅ"c’ to caller
132 pop esi ;restore esi, edi, ebx registers
133 pop edx ;as required by EBasic
134 pop ebx
135 mov esp,ebp ;restore top-of-the-stack pointer
136 pop ebp ;restore stack base pointer
137 ret 4 ;balance the stack by clearing the parameter from the stack
138 .ef
139 L00001:
140 jmp _ib_finish
141
142 segment .data use32 align=4
143 align 4
144 $STR00000 db "The results!",0
145 align 4
146 $STR00001 db "Obj1 value is: ",0
147 align 4
148 $STR00002 db "Obj2 value is: ",0
149 align 4
150 $STR00003 db "myResult value: ",0
151 align 4
152 $STR00004 db "",0
153 segment .bss use32 align=4
154 alignb 4
155 $myResult resd 1
156 alignb 4
157 $Obj2 resd 1
158 alignb 4
159 $Obj1 resd 1
winmain.lib, dllmain.lib, and crtdll.lib are automatically included in this program. The code in these libraries setup required screen buffers and heaps, perform housekeeping chores like opening/closing the console and getting handles from the OS, sorting out the various variable types to be printed and passing that data on to _sprintf to be displayed on the console screen.
I had to use the objconv.exe program to translate the three libraries into .o and .a files so I could see the actual code in the source files. Now I understand how EBASIC is able to print various types of variables, i.e., INT, STRING, CHAR, FLOAT, etc. without the programmer having to make any type conversions that would have to be done in other BASICs. For instance, in another BASIC, the programmer would have to code like this:
PRINT ("The result is: " + str$(Obj1) + "/n")
to print the string + integer value + line feed/carriage return. In EBASIC, this is all done for you--pretty neat.
EBASIC code is done simply:
PRINT "The result is: ", Obj1
Thanks Ficko, it's nice to understand what each procedure/keyword in EBASIC does and how each works down at the low level.
It also gives one a glimpse into the genius manner in which the compiler has been constructed and the overall behaviour underlying the generated code. An added benefit of understanding the code at a lowlevel, for instance, is that I would have happily gone on pushing/popping esi, ebx, and ebp every time I included inline assembly code procedures in any of my programs. Now I can see that EBASIC/NASM does this automatically; I would just be duplicating (bloating and slowing down) code.
Logman