I've just finished the preliminary test with the ASCII program, and it is
quite difficult to say how bigger the executable has become after adding
some PROCs.
I added some 300 instructions more or less, but the executable size is still 3.584 bytes
like the executable without these PROCs.
So there is surely a better way to tell how many bytes a PROC or a group of
PROCs take.
I attach the two versions of the program, before and after adding the new code.
Could somebody tell me how bigger the new prog [ascii_3.asm] is?
Thanks
Frank
Looking at the EXE is a poor approach as it often reflects the paging/alignment of the file.
Use the -Fl option to generate a LST listing file. Examine the listing as it contains details of segments and procedures.
Microsoft (R) Macro Assembler Version 6.15.8803 05/01/10 16:01:09
proc.asm Page 1 - 1
.386
.model flat,stdcall
00000000 .code
00000000 TestFunction proc
00000000 55 push ebp
00000001 8B EC mov ebp, esp
00000003 8B E5 mov esp, ebp
00000005 5D pop ebp
00000006 C3 ret 0
00000007 TestFunction endp
end
Microsoft (R) Macro Assembler Version 6.15.8803 05/01/10 16:01:09
proc.asm Symbols 2 - 1
Segments and Groups:
N a m e Size Length Align Combine Class
FLAT . . . . . . . . . . . . . . GROUP
_DATA . . . . . . . . . . . . . 32 Bit 00000000 DWord Public 'DATA'
_TEXT . . . . . . . . . . . . . 32 Bit 00000007 DWord Public 'CODE'
Procedures, parameters and locals:
N a m e Type Value Attr
TestFunction . . . . . . . . . . P Near 00000000 _TEXT Length= 00000007 Public STDCALL
Symbols:
N a m e Type Value Attr
@CodeSize . . . . . . . . . . . Number 00000000h
@DataSize . . . . . . . . . . . Number 00000000h
@Interface . . . . . . . . . . . Number 00000003h
@Model . . . . . . . . . . . . . Number 00000007h
@code . . . . . . . . . . . . . Text _TEXT
@data . . . . . . . . . . . . . Text FLAT
@fardata? . . . . . . . . . . . Text FLAT
@fardata . . . . . . . . . . . . Text FLAT
@stack . . . . . . . . . . . . . Text FLAT
0 Warnings
0 Errors
Get the linker to generate a MAP file by using the /MAP option, or -link /MAP on the MASM command line. The MAP file will get details of the actual section sizes.
Debug information also contains useful data, certain CODEVIEW and FPO records contain data about procedure size.
ASCII.LST
Segments and Groups:
N a m e Size Length Align Combine Class
FLAT . . . . . . . . . . . . . . GROUP
_BSS . . . . . . . . . . . . . . 32 Bit 00001F80 Para Public 'BSS'
_DATA . . . . . . . . . . . . . 32 Bit 00000063 Para Public 'DATA'
_TEXT . . . . . . . . . . . . . 32 Bit 000001BF Para Public 'CODE'
ASCII_3.LST
Segments and Groups:
N a m e Size Length Align Combine Class
FLAT . . . . . . . . . . . . . . GROUP
_BSS . . . . . . . . . . . . . . 32 Bit 00001F80 Para Public 'BSS'
_DATA . . . . . . . . . . . . . 32 Bit 00000065 Para Public 'DATA'
_TEXT . . . . . . . . . . . . . 32 Bit 0000032D Para Public 'CODE'
There is a lot of chaff from all the stuff you've imported in the "Procedures, parameters and locals:", it will take a minute or two to winnow that down to something more useful.
Thanks Clive, I've a lot to learn about those stuff yet.
Well, I was thinking about a simpler but perhaps unsuitable way of
counting the bytes, something like the use of the Current Address [$]
operator.
It is simpler, but I don't know if it is usable for the task:
a PROC
....
a ENDP
b PROC
....
b ENDP
c PROC
SizeProcs = ($ - a)
mov eax, SizeProcs
print "The size of a-b procs is about: "
print eax
inkey
c ENDP
The current address operator works fine with .DATA SECTION
but I don't know if it can be used for the .CODE SECTION as well. ::)
That would be workable, I'd look at the data in the object file myself.
Here is the data from the .LST files with the extraneous stuff removed.
ASCII
Procedures, parameters and locals:
N a m e Type Value Attr
AnyKey . . . . . . . . . . . . . P Near 00000194 _TEXT Length= 0000002B Public STDCALL
again . . . . . . . . . . . . L Near 00000194 _TEXT
ConsoleSize . . . . . . . . . . P Near 00000030 _TEXT Length= 00000033 Public STDCALL
DisplayFmt . . . . . . . . . . . P Near 0000016E _TEXT Length= 00000026 Public STDCALL
szFmtName . . . . . . . . . . DWord bp + 00000008
GetConsole . . . . . . . . . . . P Near 00000063 _TEXT Length= 00000054 Public STDCALL
szFmtName . . . . . . . . . . DWord bp + 00000008
HideTheCursor . . . . . . . . . P Near 000000E2 _TEXT Length= 0000002B Public STDCALL
InitProc . . . . . . . . . . . . P Near 0000010D _TEXT Length= 00000061 Public STDCALL
Main . . . . . . . . . . . . . . P Near 00000000 _TEXT Length= 00000030 Public STDCALL
finish . . . . . . . . . . . . L Near 00000028 _TEXT
ShowTheCursor . . . . . . . . . P Near 000000B7 _TEXT Length= 0000002B Public STDCALL
ASCII_3
Procedures, parameters and locals:
N a m e Type Value Attr
AnyKey . . . . . . . . . . . . . P Near 00000302 _TEXT Length= 0000002B Public STDCALL
again . . . . . . . . . . . . L Near 00000302 _TEXT
BoxLine . . . . . . . . . . . . P Near 000000F3 _TEXT Length= 00000017 Public STDCALL
FillBoxLine . . . . . . . . . L Near 00000102 _TEXT
BuildBox . . . . . . . . . . . . P Near 00000080 _TEXT Length= 00000073 Public STDCALL
Row1 . . . . . . . . . . . . . DWord bp + 00000008
Row2 . . . . . . . . . . . . . DWord bp + 0000000C
FillBorder . . . . . . . . . . L Near 000000C1 _TEXT
BuildConsole . . . . . . . . . . P Near 0000005E _TEXT Length= 00000022 Public STDCALL
ConsoleSize . . . . . . . . . . P Near 0000002B _TEXT Length= 00000033 Public STDCALL
DisplayFmt . . . . . . . . . . . P Near 000002DC _TEXT Length= 00000026 Public STDCALL
szFmtName . . . . . . . . . . DWord bp + 00000008
DisplaySequence . . . . . . . . P Near 0000010A _TEXT Length= 000000DF Public STDCALL
PrintCol . . . . . . . . . . . L Near 0000012B _TEXT
CarryOn . . . . . . . . . . . L Near 0000013F _TEXT
IncTen . . . . . . . . . . . . L Near 00000197 _TEXT
HundredCheck . . . . . . . . . L Near 000001AD _TEXT
IncHundred . . . . . . . . . . L Near 000001BF _TEXT
NextCol . . . . . . . . . . . L Near 000001C5 _TEXT
NextRow . . . . . . . . . . . L Near 000001D7 _TEXT
EndCycle . . . . . . . . . . . L Near 000001E8 _TEXT
HideTheCursor . . . . . . . . . P Near 00000250 _TEXT Length= 0000002B Public STDCALL
InitProc . . . . . . . . . . . . P Near 0000027B _TEXT Length= 00000061 Public STDCALL
Main . . . . . . . . . . . . . . P Near 00000000 _TEXT Length= 0000002B Public STDCALL
finish . . . . . . . . . . . . L Near 00000023 _TEXT
SetBufferColor . . . . . . . . . P Near 00000211 _TEXT Length= 00000014 Public STDCALL
ShowTheCursor . . . . . . . . . P Near 00000225 _TEXT Length= 0000002B Public STDCALL
WriteScreenTitle . . . . . . . . P Near 000001E9 _TEXT Length= 00000028 Public STDCALL
WriteTitle . . . . . . . . . . L Near 00000201 _TEXT
Quote from: clive on September 13, 2010, 01:01:32 AM
That would be workable, I'd look at the data in the object file myself.
Here is the data from the .LST files with the extraneous stuff removed.
ASCII
Procedures, parameters and locals:
N a m e Type Value Attr
AnyKey . . . . . . . . . . . . . P Near 00000194 _TEXT Length= 0000002B Public STDCALL
again . . . . . . . . . . . . L Near 00000194 _TEXT
ConsoleSize . . . . . . . . . . P Near 00000030 _TEXT Length= 00000033 Public STDCALL
DisplayFmt . . . . . . . . . . . P Near 0000016E _TEXT Length= 00000026 Public STDCALL
szFmtName . . . . . . . . . . DWord bp + 00000008
GetConsole . . . . . . . . . . . P Near 00000063 _TEXT Length= 00000054 Public STDCALL
szFmtName . . . . . . . . . . DWord bp + 00000008
HideTheCursor . . . . . . . . . P Near 000000E2 _TEXT Length= 0000002B Public STDCALL
InitProc . . . . . . . . . . . . P Near 0000010D _TEXT Length= 00000061 Public STDCALL
Main . . . . . . . . . . . . . . P Near 00000000 _TEXT Length= 00000030 Public STDCALL
finish . . . . . . . . . . . . L Near 00000028 _TEXT
ShowTheCursor . . . . . . . . . P Near 000000B7 _TEXT Length= 0000002B Public STDCALL
ASCII_3
Procedures, parameters and locals:
N a m e Type Value Attr
AnyKey . . . . . . . . . . . . . P Near 00000302 _TEXT Length= 0000002B Public STDCALL
again . . . . . . . . . . . . L Near 00000302 _TEXT
BoxLine . . . . . . . . . . . . P Near 000000F3 _TEXT Length= 00000017 Public STDCALL
FillBoxLine . . . . . . . . . L Near 00000102 _TEXT
BuildBox . . . . . . . . . . . . P Near 00000080 _TEXT Length= 00000073 Public STDCALL
Row1 . . . . . . . . . . . . . DWord bp + 00000008
Row2 . . . . . . . . . . . . . DWord bp + 0000000C
FillBorder . . . . . . . . . . L Near 000000C1 _TEXT
BuildConsole . . . . . . . . . . P Near 0000005E _TEXT Length= 00000022 Public STDCALL
ConsoleSize . . . . . . . . . . P Near 0000002B _TEXT Length= 00000033 Public STDCALL
DisplayFmt . . . . . . . . . . . P Near 000002DC _TEXT Length= 00000026 Public STDCALL
szFmtName . . . . . . . . . . DWord bp + 00000008
DisplaySequence . . . . . . . . P Near 0000010A _TEXT Length= 000000DF Public STDCALL
PrintCol . . . . . . . . . . . L Near 0000012B _TEXT
CarryOn . . . . . . . . . . . L Near 0000013F _TEXT
IncTen . . . . . . . . . . . . L Near 00000197 _TEXT
HundredCheck . . . . . . . . . L Near 000001AD _TEXT
IncHundred . . . . . . . . . . L Near 000001BF _TEXT
NextCol . . . . . . . . . . . L Near 000001C5 _TEXT
NextRow . . . . . . . . . . . L Near 000001D7 _TEXT
EndCycle . . . . . . . . . . . L Near 000001E8 _TEXT
HideTheCursor . . . . . . . . . P Near 00000250 _TEXT Length= 0000002B Public STDCALL
InitProc . . . . . . . . . . . . P Near 0000027B _TEXT Length= 00000061 Public STDCALL
Main . . . . . . . . . . . . . . P Near 00000000 _TEXT Length= 0000002B Public STDCALL
finish . . . . . . . . . . . . L Near 00000023 _TEXT
SetBufferColor . . . . . . . . . P Near 00000211 _TEXT Length= 00000014 Public STDCALL
ShowTheCursor . . . . . . . . . P Near 00000225 _TEXT Length= 0000002B Public STDCALL
WriteScreenTitle . . . . . . . . P Near 000001E9 _TEXT Length= 00000028 Public STDCALL
WriteTitle . . . . . . . . . . L Near 00000201 _TEXT
Thanks clive.
The New PROCs are:1) BoxLine Length= 00000017
2) BuildBox . .Length= 00000073
3) BuildConsole . . Length= 00000022
4) DisplaySequence Length= 000000DF
5) SetBufferColor . . Length= 00000014
6) WriteScreenTitle . Length= 00000028
Minus the removed ones:1) GetConsole . . Length= 00000054
Time to add and subtract. :lol
Edit: 371 bytes. Not bad, considering we started with a 8.000 bytes file. :P
And the program is not optimized for size yet.
Frank,
This is the normal methods used in code. Clive has a good point in that you need to be careful where you put the trailing label as it can pick up extra bytes of alignment padding. NOTE that the two labels are global scope labels with the trailing "::" at the end.
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
.data
item dd glbl1 - glbl0
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
print str$(item),13,10 ; static result, calculated at assembly time
mov eax, OFFSET glbl1
sub eax, OFFSET glbl0
print str$(eax),13,10 ; dynamic result, calculated at runtime
call main
inkey
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
align 16
glbl0::
main proc
print "Get Procedure Length",13,10
nop ; add an extra byte
ret
main endp
glbl1::
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
Thanks Steve, these are quite handy methods indeed. :U
The ClearScreen doesn't clear the attribute, so when I use the cls
macro, the attributes remain on the screen.
Somebody suggested a little change to the ClearScreen PROC inside the
MASM32 lib:
http://www.masm32.com/board/index.php?topic=4878.0
and it clears like DOS CLS, also the attributes.
It could be handy to make this change as the author suggested.
Something I've noticed is that the size of the prog grows 512 bytes at a time,
even if not all of them are used.
Frank
Frank,
Its easy enough for anyone interested to code up a screen clear algo that has additional attributes but the demand for fancy formating at a console level is now so low that I would not lose much sleep over it. The other factor is once an algo has been around for a while and has ben used by enough people its unwise to change it without breaking someones code.
RE the size increase, its normal depending on the file alignment set with the linker, 512 byte is common and your exe steps up in 512 byte increments.
Here is a gift from the MasmBasic library. Usage:
QuoteZeProc_s:
ZeProc proc whatever
ret
ZeProc endp
ZeProc_endp:
include \masm32\include\masm32rt.inc
MyTest PROTO: DWORD, :DWORD
CodeSize MACRO algo
pushad
mov eax, offset &algo&_endp
sub eax, offset &algo&_s
print str$(eax), 9, " bytes for &algo&", 13, 10
popad
ENDM
.code
start:
CodeSize MyTest
inkey "Cute..."
exit
MyTest_s:
MyTest proc arg1:DWORD, arg2:DWORD
MsgBox 0, arg1, arg2, MB_OK
ret
MyTest endp
MyTest_endp:
end start
Quote from: jj2007 on September 13, 2010, 07:46:41 AM
Here is a gift from the MasmBasic library. Usage:
QuoteZeProc_s:
ZeProc proc whatever
ret
ZeProc endp
ZeProc_endp:
include \masm32\include\masm32rt.inc
MyTest PROTO: DWORD, :DWORD
CodeSize MACRO algo
pushad
mov eax, offset &algo&_endp
sub eax, offset &algo&_s
print str$(eax), 9, " bytes for &algo&", 13, 10
popad
ENDM
.code
start:
CodeSize MyTest
inkey "Cute..."
exit
MyTest_s:
MyTest proc arg1:DWORD, arg2:DWORD
MsgBox 0, arg1, arg2, MB_OK
ret
MyTest endp
MyTest_endp:
end start
Thanks Jochen,
I'll give it a try. :U
Instead of the redundant label at the start of the procedure, why not just use the procedure name?
sub eax, algo
Quote from: MichaelW on September 13, 2010, 06:45:28 PM
Instead of the redundant label at the start of the procedure, why not just use the procedure name?
sub eax, algo
This is the way I'm going too. I don't know why Jochen prefer to use
another label. ::)
For my personal taste I'd prefer to use the MACRO this way:
include \masm32\include\masm32rt.inc
MyTest PROTO: DWORD, :DWORD
CodeSize MACRO algo
pushad
mov eax, offset &algo&_endp
sub eax, offset &algo&
print str$(eax), 9, " bytes for &algo&", 13, 10
popad
ENDM
.code
start:
CodeSize MyTest
inkey "Cute..."
exit
MyTest proc arg1:DWORD, arg2:DWORD
MsgBox 0, arg1, arg2, MB_OK
ret
MyTest_endp::
MyTest endp
But it is just a question of personal preference. I think it works well
both way. :U
thanks for the gift, jochen :bg
Probably because there are many ways to skin the cat. And perhaps it would allow you to account for faux alignment added to a procedure to achieve internal loop alignment where looking at the entry address will not.
In the general case I'd want to automate the tracking of procedure size based on assembler/compiler metrics that they already generate, then again if you only want stats on a few routines for printing/comparison any easy/simple solution with suffice.
Does the size of the executable in bytes +/- a few KB matter on a machine with 2 GB and allocated in quanta of 4 KB pages, I am fairly doubtful.
If you are trying to squeeze some code into a 64 KB or 128 KB ROM the code/data/bss footprints do become more pertinent. In which case the tools are generally smarter to track this kind of thing, and provide HTML reports.
When Microsoft optimizes executable images, they look to reduce the working set (WS, collection of pages most commonly used together), and analyze the call frequency and proximity of subroutines being used together, vs those parts of the code which are barely/rarely visited.
I'm trying to squeeze an 8K external file into 250 bytes of code more or less, like a
compressor from DATA to CODE, so all this ADO is about that thing :P
The MACRO and methods proposed are quite suitable for the task, and
at the same time, looking at the LST/MAP files is something that I need to
learn anyway in order to understand something more about assembling and
linking process as well. :U
Quote from: MichaelW on September 13, 2010, 06:45:28 PM
Instead of the redundant label at the start of the procedure, why not just use the procedure name?
sub eax, algo
Michael,
I can't remember the exact conditions, but I have seen cases where that resulted in wrong sizes because a label close to the module entry point was used that contained a jmp MyProc. Since then I use the extra MyProc_s: label.
This is probably the way I'm going to use the customized version of Jochen's MACRO:
;-------------------------------------------------------
; Jochen's MACRO for calculating the size of a PROC
; customized for my personal taste/need. 13-sep-2010
;-------------------------------------------------------
include \masm32\include\masm32rt.inc
MyTest PROTO: DWORD, :DWORD
MyCodeSize MACRO algo, algosize
pushad
mov eax, offset &algo&End
sub eax, offset &algo&
mov &algosize&, eax
popad
ENDM
.data?
MyProcSize DWORD ?
.code
start:
Main PROC
MyCodeSize MyProc, MyProcSize
print str$(MyProcSize), 9, " bytes for MyProc", 13, 10, 13, 10
inkey "Press a key to continue..."
invoke ExitProcess, 0
Main ENDP
;-------------------------------------------------------
; PROC that we want to calculate the size of
;-------------------------------------------------------
MyProc proc arg1:DWORD, arg2:DWORD
MsgBox 0, arg1, arg2, MB_OK
ret
MyProcEnd::
MyProc endp
I prefer to store the size of the PROC in a variable for future use.
Do you see any problem that can arise?
Frank