October 31, 2025, 11:49:26 AM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Source Code is Driving me Crazy!

Started by Logman, October 29, 2009, 08:40:07 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Logman

October 29, 2009, 08:40:07 AM Last Edit: October 30, 2009, 06:20:49 AM by Logman
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
Education is what you get when you read the fine print.<br />Experience is what you get when you don't!

Ficko

October 29, 2009, 10:33:28 AM #1 Last Edit: October 29, 2009, 02:37:56 PM by Ficko
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

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

Logman

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
Education is what you get when you read the fine print.<br />Experience is what you get when you don't!

Logman

October 30, 2009, 05:26:30 AM #3 Last Edit: October 30, 2009, 07:03:37 AM by 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
Education is what you get when you read the fine print.<br />Experience is what you get when you don't!