Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I want to know the real content of binary file.
File was created by Deplhi (FreePascal?) based application.
Filename is FDane.bin
I don't have source code of this app
After disassembling application i see that (part of disassembled code that contain FDane.bin word):
procedure TFrmDroga.ReadLinesFromFile(Sender : TObject);
begin
(*
005F0BB0 55 push ebp
005F0BB1 8BEC mov ebp, esp
005F0BB3 83C4E0 add esp, -$20
005F0BB6 53 push ebx
005F0BB7 56 push esi
005F0BB8 57 push edi
005F0BB9 8945FC mov [ebp-$04], eax
005F0BBC 8D75EF lea esi, [ebp-$11]
005F0BBF 33C0 xor eax, eax
005F0BC1 55 push ebp
005F0BC2 681A135F00 push $005F131A
005F0BC7 64FF30 push dword ptr fs:[eax]
005F0BCA 648920 mov fs:[eax], esp
|
005F0BCD E8DAC4E1FF call 0040D0AC
005F0BD2 DD1D6C936000 fstp qword ptr [$0060936C]
005F0BD8 9B wait
005F0BD9 B201 mov dl, $01
* Reference to class TMemoryStream
|
005F0BDB A144EB4100 mov eax, dword ptr [$0041EB44]
|
005F0BE0 E84735E1FF call 0040412C
005F0BE5 8945F8 mov [ebp-$08], eax
005F0BE8 B201 mov dl, $01
* Reference to class TMemoryStream
|
005F0BEA A144EB4100 mov eax, dword ptr [$0041EB44]
|
005F0BEF E83835E1FF call 0040412C
005F0BF4 8945F4 mov [ebp-$0C], eax
* Possible String Reference to: 'FDane.bin'
|
005F0BF7 BA30135F00 mov edx, $005F1330
005F0BFC 8B45F4 mov eax, [ebp-$0C]
|
005F0BFF E8C834E3FF call 004240CC
005F0C04 6A00 push $00
005F0C06 6A00 push $00
005F0C08 8B45F8 mov eax, [ebp-$08]
|
005F0C0B E8EC2CE3FF call 004238FC
005F0C10 6A00 push $00
005F0C12 6A00 push $00
005F0C14 8B45F4 mov eax, [ebp-$0C]
|
005F0C17 E8E02CE3FF call 004238FC
005F0C1C 8B45F4 mov eax, [ebp-$0C]
005F0C1F 8B10 mov edx, [eax]
005F0C21 FF12 call dword ptr [edx]
005F0C23 85C0 test eax, eax
005F0C25 7E3B jle 005F0C62
005F0C27 8945E8 mov [ebp-$18], eax
005F0C2A BB01000000 mov ebx, $00000001
005F0C2F 8BD6 mov edx, esi
005F0C31 B901000000 mov ecx, $00000001
005F0C36 8B45F4 mov eax, [ebp-$0C]
005F0C39 8B38 mov edi, [eax]
* Possible reference to virtual method TMemoryStream.OFFS_0C
|
005F0C3B FF570C call dword ptr [edi+$0C]
005F0C3E 8BC3 mov eax, ebx
005F0C40 B9C8000000 mov ecx, $000000C8
005F0C45 99 cdq
005F0C46 F7F9 idiv ecx
005F0C48 80C220 add dl, $20
005F0C4B 3016 xor [esi], dl
005F0C4D 8BD6 mov edx, esi
005F0C4F B901000000 mov ecx, $00000001
005F0C54 8B45F8 mov eax, [ebp-$08]
005F0C57 8B38 mov edi, [eax]
* Possible reference to virtual method TMemoryStream.OFFS_10
|
005F0C59 FF5710 call dword ptr [edi+$10]
005F0C5C 43 inc ebx
005F0C5D FF4DE8 dec dword ptr [ebp-$18]
005F0C60 75CD jnz 005F0C2F
005F0C62 6A00 push $00
005F0C64 6A00 push $00
005F0C66 8B45F8 mov eax, [ebp-$08]
|
005F0C69 E88E2CE3FF call 004238FC
005F0C6E 8B45F4 mov eax, [ebp-$0C]
|
005F0C71 E80634E3FF call 0042407C
005F0C76 8B45FC mov eax, [ebp-$04]
* Reference to control TFrmDroga.CDSBrutto : TClientDataSet
|
005F0C79 8B8098040000 mov eax, [eax+$0498]
005F0C7F 8B55F8 mov edx, [ebp-$08]
|
005F0C82 E8A180F0FF call 004F8D28
005F0C87 8B45FC mov eax, [ebp-$04]
* Reference to control TFrmDroga.CDSBrutto : TClientDataSet
|
005F0C8A 8B8098040000 mov eax, [eax+$0498]
After using 'strings FDane.bin | head -n 50' get (this is a part):
&'(1*+,*.
0120456
82s_f\UM%27
6GFFHIJKLB
>6)5?#
,8-05_^^`abcdn*
srrtuvwxq
!"#$%hg,)g
./0323446789:;<s~G#ABCDEFGH
BL{~sm
nbfeVWXZZ[\_^_`abcd;&
hijklmno
2ytDDGDD7GMEN
Re,'
2342678?:;<=>?
EEFGHIJK
EPbdchh
klkj[\]V_`aecdefgh)
lnopqrstu
7ryNILAC2
s"!"#$%&'
7896;<=5?#ABCD
KJKLMNOP
^U`aheg
`jlo`abndefkhijklm
0}qstuvwxy
<w~H
&&'()*+,-./61
z89:*<<>?#ABCDEFGHuJKLMNOPQR
doj[\]L_aaccdefghi$+
mnopqrstu(7qyLK##3C
!"#$%&
Zi +
678/::<8>?#ABC
/IIJKLMNO
YTffgdd
gokn_`aucee`ghijkl
prstuvwx9
;v}MI
b{&%&'()*+
;<=%?AAHCDEFGH
ONOPQRST
RYlklac
\WTSdef{hhj`lmnopq
twxyz{|}
!"#$e
**+,-./0
#ABcDDFHHIJKLMn
QSTUVWXY
V^fPQ^^)YWWXYjklLnnparstuvw8
After 200 lines data changes to this:
MKEUNF/0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWV
5797;
ghijklmnopqrstuvwxyz{|}~
!"#$%&7cFFNF
]AAF]V89:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[G
xyz{|}~
!"#$%&'()*;gBBJZT
a[FO]KRS^<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_x1)3D,
_R T
Vyz{|}~
!"#$%&'()*+,-.
cTDDBXMHW\
t/-')d
)-)3.$;,n
r)t:x8vYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcU5-7H:
!"#$%&'()*+,-./012-da}
qW\I]NJM5*666$f
4,!9:RSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgs,
Z(5856
!"#$%&'()*+,-./0123456:snx
EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
iyi|v{123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkg&9$P93?1846xyz{|}~
!"#$%&'()*+,-./0123456789:!f\U
!%;c
?)3'>/k
VWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
ibg#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnoj+"S2'7#+:2:?5^
!"#$%&'()*+,-./0123456789:;<=>2
MNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrsS&
!"#$%&'()*+,-./0123456789:;<=>?#AB_
*6&$'#.l
+#17;!!u
`abcdefghijklmnopqrstuvwxyz{|}~
OVLJ
aikfh
456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwX#
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFf
-)7o
97>=6,9=y
55:D6H&Fijklmnopqrstuvwxyz{|}~
HDOJG_HB
yegenk
456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{N6
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJB
UVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
idolslr'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNF
YZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
It's looks like there are some character data (I see ASCII up to 127 characters). I'm not a Pascal, Delphi programmer. I know Python, some C and Java. Is it possible to decode ?
Some tips:
The disassembly shows tmemorystream, and then tclientdataset calls. This makes it delphi, and delphi/bcb alone (FreePascal's equivalent is called TBufDataset)
TClientdataset .cds is some proprietary streaming format of a dataset. It might be delphi version dependent. Later (D2010+? rad studio only?) versions come with TClientDataset sources which you could inspect.
Searching for ".cds tclientdataset file format" might also yield something, and hope it doesn't support encryption.
Related
After the upgrade from "TMS ComponentPack" to "TMS VCL UI Pack", the application installed on Windows 7 or windows 8.1, crashes after start with the error RunTime Error 217.
The same application works on windows 10 or windows server 2012
Using EurekaLog, I can see that the specific error is Assertion Failure on AdvXPVS.pas at line 1536 during call procedure LOADDLL
I cannot debug the application becouse of my development platform is installed on windows 10.
procedure LoadDLL;
begin
if DLLLoaded then Exit;
DLLHandle := LoadLibrary('UXTHEME.DLL');
if DLLHandle >= 32 then
begin
DLLLoaded := True;
#OpenThemeDataForDpi := GetProcAddress(DLLHandle,'OpenThemeDataForDpi');
Assert(#OpenThemeDataForDpi <> nil);
//*********************************************************
// LINE 1536 START FROM HERE
//*********************************************************
#OpenThemeData := GetProcAddress(DLLHandle,'OpenThemeData');
Assert(#OpenThemeData <> nil);
#CloseThemeData := GetProcAddress(DLLHandle,'CloseThemeData');
Assert(#CloseThemeData <> nil);
#DrawThemeBackground := GetProcAddress(DLLHandle,'DrawThemeBackground');
Assert(#DrawThemeBackground <> nil);
...
...
#EnableTheming := GetProcAddress(DLLHandle,'EnableTheming');
Assert(#EnableTheming <> nil);
end
else
begin
DLLLoaded := False;
{ Error: UXTHEME.DLL could not be loaded !! }
end;
end;
Assembler Information:
---------------------------------------------------------------------------
; Base Address: $1012000, Allocation Base: $400000, Region Size: 10563584
; Allocation Protect: PAGE_EXECUTE_WRITECOPY, Protect: PAGE_EXECUTE_READ
; State: MEM_COMMIT, Type: MEM_IMAGE
;
;
; AdvXPVS.LoadDLL (Line=0 - Offset=33)
; ------------------------------------
01012459 8903 MOV [EBX], EAX
0101245B 833B20 CMP DWORD PTR [EBX], $20
0101245E 0F825F080000 JB +$085F ; ($01012CC3) AdvXPVS.LoadDLL (Line=0)
01012464 C605CC26A60101 MOV BYTE PTR [$01A626CC], 1
0101246B 68E82C0101 PUSH $01012CE8 ; ($01012CE8) AdvXPVS.LoadDLL (Line=0) Data as ANSI: 'O'; Data as UNICODE: 'OpenThemeDataFor...' AdvXPVS.LoadDLL (Line=0)
01012470 8B03 MOV EAX, [EBX]
01012472 50 PUSH EAX
01012473 E864DE40FF CALL -$BF219C ; ($004202DC) Winapi.GetProcAddress
01012478 8BF0 MOV ESI, EAX
0101247A 8935C844BA01 MOV [$01BA44C8], ESI
01012480 85F6 TEST ESI, ESI
01012482 7514 JNZ +$14 ; ($01012498) AdvXPVS.LoadDLL (Line=0)
01012484 B900060000 MOV ECX, $00000600
01012489 BA1C2D0101 MOV EDX, $01012D1C ; ($01012D1C) AdvXPVS.LoadDLL (Line=0) UNICODE: 'C:\Users\Gianluca\Documents\tmssoftware\TMS VCL UI Pack\AdvXPVS.pas' AdvXPVS.LoadDLL (Line=0)
0101248E B8B02D0101 MOV EAX, $01012DB0 ; ($01012DB0) AdvXPVS.LoadDLL (Line=0) UNICODE: 'Assertion failure' AdvXPVS.LoadDLL (Line=0)
;
; Line=0 - Offset=91
; ------------------
01012493 E800A53FFF CALL -$C05B00 ; ($0040C998) System._Assert ; <-- EXCEPTION
01012498 68D42D0101 PUSH $01012DD4 ; ($01012DD4) AdvXPVS.LoadDLL (Line=0) Data as ANSI: 'O'; Data as UNICODE: 'OpenThemeData' AdvXPVS.LoadDLL (Line=0)
0101249D 8B03 MOV EAX, [EBX]
0101249F 50 PUSH EAX
010124A0 E837DE40FF CALL -$BF21C9 ; ($004202DC) Winapi.GetProcAddress
010124A5 8BF0 MOV ESI, EAX
010124A7 8935CC44BA01 MOV [$01BA44CC], ESI
010124AD 85F6 TEST ESI, ESI
010124AF 7514 JNZ +$14 ; ($010124C5) AdvXPVS.LoadDLL (Line=0)
010124B1 B903060000 MOV ECX, $00000603
010124B6 BA1C2D0101 MOV EDX, $01012D1C ; ($01012D1C) AdvXPVS.LoadDLL (Line=0) UNICODE: 'C:\Users\Gianluca\Documents\tmssoftware\TMS VCL UI Pack\AdvXPVS.pas' AdvXPVS.LoadDLL (Line=0)
010124BB B8B02D0101 MOV EAX, $01012DB0 ; ($01012DB0) AdvXPVS.LoadDLL (Line=0) UNICODE: 'Assertion failure' AdvXPVS.LoadDLL (Line=0)
010124C0 E8D3A43FFF CALL -$C05B2D ; ($0040C998) System._Assert
010124C5 68F02D0101 PUSH $01012DF0 ; ($01012DF0) AdvXPVS.LoadDLL (Line=0) Data as ANSI: 'C'; Data as UNICODE: 'CloseThemeData' AdvXPVS.LoadDLL (Line=0)
010124CA 8B03 MOV EAX, [EBX]
010124CC 50 PUSH EAX
010124CD E80ADE40FF CALL -$BF21F6 ; ($004202DC) Winapi.GetProcAddress
Call Stack Information:
-------------------------------------------------------------------------------------------------------------------------------------
|Methods |Details|Stack |Address |Module |Offset |Source |Unit |Class|Procedure/Method |Line |
-------------------------------------------------------------------------------------------------------------------------------------
|*Exception Thread: ID=596; Parent=0; Priority=0 |
|Class=; Name=MAIN |
|DeadLock=0; Wait Chain= |
|Comment= |
|-----------------------------------------------------------------------------------------------------------------------------------|
|7FFFFFFE|03 |00000000|01012493|testxHairPlus.exe|00C12493|AdvXPVS.pas |AdvXPVS | |LoadDLL | |
|00000060|03 |0018FF34|019F4A28|testxHairPlus.exe|015F4A28|AdvXPVS.pas |AdvXPVS | |Initialization | |
|00000060|03 |0018FF3C|0040C512|testxHairPlus.exe|0000C512|System.pas |System | |InitUnits | |
|00000060|03 |0018FF60|0040C57B|testxHairPlus.exe|0000C57B|System.pas |System | |_StartExe | |
|00000020|03 |0018FF64|004147CE|testxHairPlus.exe|000147CE|SysInit.pas |SysInit | |_InitExe | |
|00004020|03 |0018FF6C|01A2435F|testxHairPlus.exe|0162435F|xHairPlus.pas|xHairPlus| |Initialization | |
|7FFF7FFE|03 |0018FF84|75027C02|kernel32.dll |00017C02|KERNEL32.DLL |KERNEL32 | |BaseThreadInitThunk| |
-------------------------------------------------------------------------------------------------------------------------------------
Sometimes we get errors with line numbers off by one. In your case Assert(#OpenThemeDataForDpi <> nil); is failing because OpenThemeDataForDpi needs at least Windows 10 1703. As workaround you can write your own version which just ignores dpi argument and calls standard OpenThemeData, then when you get nil pointer set it to your implementation.
I want to have universal way of detecting specific CPU features. For this task I've created this function which takes EAX leaf number,Register name and bit number and returns true or false. It works fine for MMX/SSEx/AVX (EAX=1) but it does not detect AVX2 (EAX=7).
CPU: i5-4670k
OS: Windows 7
DetectCPUFeature('1','EDX',23) //DETECTS MMX CORRECTLY
DetectCPUFeature('1','EDX',25) //DETECTS SSE CORRECTLY
DetectCPUFeature('1','EDX',26) //DETECTS SSE2 CORRECTLY
DetectCPUFeature('1','ECX',0) //DETECTS SSE3 CORRECTLY
DetectCPUFeature('1','ECX',9) //DETECTS SSSE3 CORRECTLY
DetectCPUFeature('1','ECX',19) //DETECTS SSE4.1 CORRECTLY
DetectCPUFeature('1','ECX',20) //DETECTS SSE4.2 CORRECTLY
DetectCPUFeature('1','ECX',28) //DETECTS AVX CORRECTLY
DetectCPUFeature('7','EBX',5) //DOES NOT DETECT AVX2!
.
function DetectCPUFeature(EAX_Leaf_HEX,Register_Name:string;Bit:byte):boolean;
var _eax,_ebx,_ecx,_edx,EAX_Leaf,_Result: Longword;
x:integer;
Binary_mask:string;
Decimal_mask:int64;
begin
EAX_Leaf:=HexToInt(EAX_Leaf_HEX);
Binary_mask:='1';
for x:=1 to Bit do Binary_mask:=Binary_mask+'0';
Decimal_mask:=BinToInt(Binary_mask);
if AnsiUpperCase(Register_Name)='EDX' then
begin
asm
mov eax,EAX_Leaf // https://en.wikipedia.org/wiki/CPUID
db $0F,$A2 // db $0F,$A2 = CPUID instruction
mov _Result,edx
end;
end;
if AnsiUpperCase(Register_Name)='ECX' then
begin
asm
mov eax,EAX_Leaf
db $0F,$A2
mov _Result,ecx
end;
end;
if AnsiUpperCase(Register_Name)='EBX' then
begin
asm
mov eax,EAX_Leaf
db $0F,$A2
mov _Result,ebx
end;
end;
if (_Result and Decimal_mask) = Decimal_mask then DetectCPUFeature:=true
else DetectCPUFeature:=false;
end;
This sort of code is very dubious, mixing asm with Pascal code. Your code, in the asm blocks modifies registers and fails to restore them. That could easily be conflicting with the compiler's register usage. My strong advice to you is that you should never mix asm and Pascal in this way. Always use pure Pascal or pure asm.
What you need is a function that will perform the CPUID instruction and return you all the registers in a structure. You can then pick out what you want from that using Pascal code.
In addition, as #J... points out, you need to specify the sub-leaf value in the ECX register before invoking the CPUID instruction. That is a requirement for a number of the more recently added CPUID arguments.
This is the function you need:
type
TCPUID = record
EAX: Cardinal;
EBX: Cardinal;
ECX: Cardinal;
EDX: Cardinal;
end;
function GetCPUID(Leaf, Subleaf: Cardinal): TCPUID;
asm
push ebx
push edi
mov edi, ecx
mov ecx, edx
cpuid
mov [edi+$0], eax
mov [edi+$4], ebx
mov [edi+$8], ecx
mov [edi+$c], edx
pop edi
pop ebx
end;
I've written this for 32 bit code, but if you need to support 64 bit code also that support is easy enough to add.
function GetCPUID(Leaf, Subleaf: Integer): TCPUID;
asm
{$IF Defined(CPUX86)}
push ebx
push edi
mov edi, ecx
mov ecx, edx
cpuid
mov [edi+$0], eax
mov [edi+$4], ebx
mov [edi+$8], ecx
mov [edi+$c], edx
pop edi
pop ebx
{$ELSEIF Defined(CPUX64)}
mov r9,rcx
mov ecx,r8d
mov r8,rbx
mov eax,edx
cpuid
mov [r9+$0], eax
mov [r9+$4], ebx
mov [r9+$8], ecx
mov [r9+$c], edx
mov rbx, r8
{$ELSE}
{$Message Fatal 'GetCPUID has not been implemented for this architecture.'}
{$IFEND}
end;
With this at hand you can call CPUID passing any value as input, and retrieve all 4 registers of output, with which you can then do whatever you please.
Your code to create a bitmask is extremely inefficient and very far from idiomatic. Use 1 shl N to create a value with a single bit set, in position N.
Code like this:
if (_Result and Decimal_mask) = Decimal_mask then DetectCPUFeature:=true
else DetectCPUFeature:=false;
is also some way from idiomatic. That would normally be written like this:
DetectCPUFeature := value and mask <> 0;
You might end up with a wrapper function that looks like this:
type
TCPUIDRegister = (regEAX, regEBX, regECX, regEDX);
function GetCPUIDRegister(CPUID: TCPUID; Reg: TCPUIDRegister): Cardinal;
begin
case Reg of
regEAX:
Result := CPUID.EAX;
regEBX:
Result := CPUID.EBX;
regECX:
Result := CPUID.ECX;
regEDX:
Result := CPUID.EDX;
end;
end;
function CPUFeatureEnabled(Leaf, Subleaf: Cardinal; Reg: TCPUIDRegister; Bit: Integer): Boolean;
var
value: Cardinal;
begin
value := GetCPUIDRegister(GetCPUID(Leaf, Subleaf), Reg);
Result := value and (1 shl Bit) <> 0;
end;
While David's answer is excellent, the reason the function fails is that the ECX register is not set to zero (required for fetching extended info in the CPUID call).
See : How to detect New Instruction support in the 4th generation Intel® Core™ processor family
where AVX2 is found by (emphasis mine)
CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5]==1
The following correctly returns the extended information and identifies AVX2 support.
if AnsiUpperCase(Register_Name)='EBX' then
begin
asm
push ecx { push ecx to stack}
mov ecx, 0 { set ecx to zero}
mov eax,EAX_Leaf
db $0F,$A2
mov _Result,ebx
pop ecx { restore ecx}
end;
The other asm functions have the same error as ECX is required to be zero for those calls also.
taken from Synopse Informatique:
type
/// the potential features, retrieved from an Intel CPU
// - see https://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits
TALIntelCpuFeature =
( { in EDX }
cfFPU, cfVME, cfDE, cfPSE, cfTSC, cfMSR, cfPAE, cfMCE,
cfCX8, cfAPIC, cf_d10, cfSEP, cfMTRR, cfPGE, cfMCA, cfCMOV,
cfPAT, cfPSE36, cfPSN, cfCLFSH, cf_d20, cfDS, cfACPI, cfMMX,
cfFXSR, cfSSE, cfSSE2, cfSS, cfHTT, cfTM, cfIA64, cfPBE,
{ in ECX }
cfSSE3, cfCLMUL, cfDS64, cfMON, cfDSCPL, cfVMX, cfSMX, cfEST,
cfTM2, cfSSSE3, cfCID, cfSDBG, cfFMA, cfCX16, cfXTPR, cfPDCM,
cf_c16, cfPCID, cfDCA, cfSSE41, cfSSE42, cfX2A, cfMOVBE, cfPOPCNT,
cfTSC2, cfAESNI, cfXS, cfOSXS, cfAVX, cfF16C, cfRAND, cfHYP,
{ extended features in EBX, ECX }
cfFSGS, cf_b01, cfSGX, cfBMI1, cfHLE, cfAVX2, cf_b06, cfSMEP, cfBMI2,
cfERMS, cfINVPCID, cfRTM, cfPQM, cf_b13, cfMPX, cfPQE, cfAVX512F,
cfAVX512DQ, cfRDSEED, cfADX, cfSMAP, cfAVX512IFMA, cfPCOMMIT,
cfCLFLUSH, cfCLWB, cfIPT, cfAVX512PF, cfAVX512ER, cfAVX512CD,
cfSHA, cfAVX512BW, cfAVX512VL, cfPREFW1, cfAVX512VBMI);
/// all features, as retrieved from an Intel CPU
TALIntelCpuFeatures = set of TALIntelCpuFeature;
var
/// the available CPU features, as recognized at program startup
ALCpuFeatures: TALIntelCpuFeatures;
{**}
type
_TRegisters = record
eax,ebx,ecx,edx: cardinal;
end;
{***************************************************************}
procedure _GetCPUID(Param: Cardinal; var Registers: _TRegisters);
{$IF defined(CPU64BITS)}
asm // ecx=param, rdx=Registers (Linux: edi,rsi)
.NOFRAME
mov eax, ecx
mov r9, rdx
mov r10, rbx // preserve rbx
xor ebx, ebx
xor ecx, ecx
xor edx, edx
cpuid
mov _TRegisters(r9).&eax, eax
mov _TRegisters(r9).&ebx, ebx
mov _TRegisters(r9).&ecx, ecx
mov _TRegisters(r9).&edx, edx
mov rbx, r10
end;
{$else}
asm
push esi
push edi
mov esi, edx
mov edi, eax
pushfd
pop eax
mov edx, eax
xor eax, $200000
push eax
popfd
pushfd
pop eax
xor eax, edx
jz #nocpuid
push ebx
mov eax, edi
xor ecx, ecx
cpuid
mov _TRegisters(esi).&eax, eax
mov _TRegisters(esi).&ebx, ebx
mov _TRegisters(esi).&ecx, ecx
mov _TRegisters(esi).&edx, edx
pop ebx
#nocpuid:
pop edi
pop esi
end;
{$ifend}
{******************************}
procedure _TestIntelCpuFeatures;
var regs: _TRegisters;
begin
regs.edx := 0;
regs.ecx := 0;
_GetCPUID(1,regs);
PIntegerArray(#ALCpuFeatures)^[0] := regs.edx;
PIntegerArray(#ALCpuFeatures)^[1] := regs.ecx;
_GetCPUID(7,regs);
PIntegerArray(#ALCpuFeatures)^[2] := regs.ebx;
PByteArray(#ALCpuFeatures)^[12] := regs.ecx;
end;
initialization
_TestIntelCpuFeatures;
Essentially what I wish to do (this is for an assignment) is have the user type in a character, and then display whether its an upper case/lower case letter, a 0-9 decimal, printable symbol, or a control key.
Unfortunately while the documentation gives me an idea as to fiddle with the control logic (if/else, loops, etc) actually knowing the commands to get NASM to do what I want it to do has been frustrating as the slides and class notes, and the documentation has not been entirely constructive.
Control Keys I believe are esc, null, tab, etc from DEC 0 to 32.
In my code below, I accept input from the user, but I don't think it accepts control keys, how would I fiddle it to do so?
segment .data
msg3 dw '10h'
segment .bss
;
aChar resb 2 ; reserve 10 bytes for aChar
segment .text
global _start
_start:
; ### Code for simple Input ###
mov eax, 3 ; select kernal call #3
mov ebx, 0 ; default input device
mov ecx, aChar ; pointer to aChar'
int 0x80 ; invoke kernal call to read
; ### Code to spit the input to the screen ###
mov eax, 4 ; select kernal call #4
mov ebx, 1 ; default output device
mov ecx, aChar ; pointer to ID
int 0x80 ; write id read
; termination code
exit: mov ax, 1 ; select system call #1 system exit
mov bx, 0 ; 0 means normal
int 0x80 ; invoke kernal call
So what I am thinking, assuming I am able to accept control keys in ascii. Is that once a user inputs a character, I convert it to hex or decimal, whichever is easier and then run if else cases wherein if its between certain values its an X, and then display X, if its between 2 different values its an Y and so on.
Since something less than 33 is clearly a control key I think, (is DEL a control key? Its DEC 127), but anything less than 48 is a symbol I think, but it also has to be more than 32, or do I just implicitly assume if it greater than 32, but less than 48, assume symbol? Annoying how everything seems to inhabit different portions of the ASCII chart.
Here's the code for converting ASCII to HEX, I think decimal might've been easier to work with, but I have no idea how to a) figure out even if I have binary value of the ascii character in a registry somewhere and b) convert it to decimal so since I have this code might as well use it?
;Hex equivalent of characters HEX2CHAR.ASM
;
; Objective: To print the hex equivalent of
; ASCII character code. Demonstrates
; the use of xlat instruction.
; Input: Requests a character from the user.
; Output: Prints the ASCII code of the
; input character in hex.
%include "io.mac"
.DATA
char_prompt db "Please input a character: ",0
out_msg1 db "The ASCII code of '",0
out_msg2 db "' in hex is ",0
query_msg db "Do you want to quit (Y/N): ",0
; translation table: 4-bit binary to hex
hex_table db "0123456789ABCDEF"
.CODE
.STARTUP
read_char:
PutStr char_prompt ; request a char. input
GetCh AL ; read input character
PutStr out_msg1
PutCh AL
PutStr out_msg2
mov AH,AL ; save input character in AH
mov EBX,hex_table; EBX = translation table
shr AL,4 ; move upper 4 bits to lower half
xlatb ; replace AL with hex digit
PutCh AL ; write the first hex digit
mov AL,AH ; restore input character to AL
and AL,0FH ; mask off upper 4 bits
xlatb
PutCh AL ; write the second hex digit
nwln
PutStr query_msg ; query user whether to terminate
GetCh AL ; read response
cmp AL,'Y' ; if response is not 'Y'
jne read_char ; read another character
done: ; otherwise, terminate program
.EXIT
From "Guide to Assembly Programming in Linux".
My query is, if the code converts it to hex, is it actually in memory as hex somewhere? Or is it just displayed as ascii character representation of the hex? That obviously doesn't help me if so.
So my questions are in order:
1.How do I accept user input of a control key.
2. How do I parse ascii so that I have in memory register somewhere which I suppose is a fancy way of saying "variable" the HEX value of the ASCII character I typed in? Which I can then fiddle with and evaluate to display what I need to display?
edit: I think I found a solution, the problem is it doesn't work and I don't know why:
decimal:
mov ebx, 30h ; smallest decimal ASCII
mov edx, key
cmp edx, ebx
jl uppercase
mov ebx, 39h ; test against 9
cmp edx, ebx
jg exit
mov eax, 4
mov ebx, 1
mov ecx, decimalKey
mov edx, decimalLen
int 0x80
It just jumps to "exit" evaluating as "greater than" each time, no matter what I input.
Figured it out finally.
;
; This is assignment 2, it takes key strokes from the user
; and displays whether it is a control key, upper case,
; lower case, a printable symbol, or a decimal.
; By Blayne Elison Bradley, 9688994
; March 4th 2013
segment .data
msg db 'Enter a character: ', 0xA ; text message
len equ $-msg ; length of msg
msg2 db 'Your character is: ' ; text message
len2 equ $-msg2
controlKey: db "Control Key", 10
controlLen: equ $-controlKey
control2Key: db "ControlKey2", 10
control2Len: equ $-control2Key
printableKey: db "Printable", 10
printableLen: equ $-printableKey
printable2Key: db "Printable-2", 10
printable2Len: equ $-printable2Key
printable3Key: db "Printable-3", 10
printable3Len: equ $-printable3Key
printable4Key: db "Printable-4", 10
printable4Len: equ $-printable4Key
decimalKey: db "Decimal", 10
decimalLen: equ $-decimalKey
upperKey: db "Upper Case", 10
upperLen: equ $-upperKey
lowerKey: db "Lower Case", 10
lowerLen: equ $-lowerKey
smallerKey: db "Smaller", 10
smallerLen: equ $-smallerKey
biggerKey: db "Bigger", 10
biggerLen: equ $-biggerKey
segment .bss
;
aChar resb 8 ; reserve 8 bytes for aChar
; I changed the above to 8 and that seems to work with the code below,
; I don't know if its crucial to its execution.
segment .text
global _start
_start:
; ### Code for Outputting a simple Message ###
mov eax, 4 ; select kernal call #4
mov ebx, 1 ; default output device
mov ecx, msg ; second argument; pointer to message
mov edx, len ; third argument: length
int 0x80 ; invoke kernal call to write
mov eax, 3 ; select kernal call #3
mov ebx, 0 ; default input device
mov ecx, aChar ; pointer to aChar'
int 0x80 ; invoke kernal call to read
control: ; is it a control key?
mov al, [aChar] ;
cmp al, 1Fh
jg printable
; Output
mov eax, 4
mov ebx, 1
mov ecx, controlKey
mov edx, controlLen
int 0x80
jmp exit ; because duh
decimal:
mov al, [aChar] ;
cmp al, '9'
jg printable2
; Output
mov eax, 4
mov ebx, 1
mov ecx, decimalKey
mov edx, decimalLen
int 0x80
jmp exit
printable:
mov al, [aChar] ;
cmp al, '/'
jg decimal
; Output
mov eax, 4
mov ebx, 1
mov ecx, controlKey
mov edx, controlLen
int 0x80
jmp exit ; because duh
printable2:
mov al, [aChar] ;
cmp al, '#'
jg uppercase
; Output
mov eax, 4
mov ebx, 1
mov ecx, printable2Key
mov edx, printable2Len
int 0x80
jmp exit ; because duh
uppercase:
mov al, [aChar] ;
cmp al, 'Z'
jg printable3
; Output
mov eax, 4
mov ebx, 1
mov ecx, upperKey
mov edx, upperLen
int 0x80
jmp exit ; because duh
printable3:
mov al, [aChar] ;
cmp al, '`'
jg lowercase
; Output
mov eax, 4
mov ebx, 1
mov ecx, printable3Key
mov edx, printable3Len
int 0x80
jmp exit ; because duh
lowercase:
mov al, [aChar] ;
cmp al, 7Ah
jg printable4
; Output
mov eax, 4
mov ebx, 1
mov ecx, lowerKey
mov edx, lowerLen
int 0x80
jmp exit ; because duh
printable4:
mov al, [aChar] ;
cmp al, '~'
jg control2
; Output
mov eax, 4
mov ebx, 1
mov ecx, printable4Key
mov edx, printable4Len
int 0x80
jmp exit ; because duh
control2: ; is it a control key?
mov al, [aChar] ;
cmp al, 7Fh
jg exit ; beyond range
; Output
mov eax, 4
mov ebx, 1
mov ecx, control2Key
mov edx, control2Len
int 0x80
jmp exit ; because duh
exit:
mov eax, 1
xor ebx, ebx
int 0x80
In Delphi how can I detect overflow errors for Int64?
For Integers we could do:
type
MyInt = Integer; //Int64
function TryMaxTimes10(out Res: MyInt): boolean;
var
a, b: MyInt;
begin
{$Q+}
try
a := High(MyInt);
b := 10;
Res := a * b; //REF1
Result := True;
except
Result := False;
end;
{$Q-}
end;
For MyInt = Integer, line REF1 gives an exception and so TryMaxTimes10 returns false.
But if we change MyInt to MyInt = Int64, then REF1 does not give an exception and TryMaxTimes10 returns true!
I understand that the help for {$Q+} does not specifically mention Int64: ... {$Q+} state, certain integer arithmetic operations ... are checked for overflow.
QUESTION: So my question is, how can we detect overflow errors for Int64?
(I'm using Delphi 7. Does the same thing happen in newer versions of Delphi?)
This is a known issue. See http://qc.embarcadero.com/wc/qcmain.aspx?d=10185, and the comments Andy wrote at the bottom.
My suggestion would be to create a function (I did not compile nor test this - just an example):
function Foo(A, B : Int64) : Int64;
var bNeg : boolean;
begin
// Do we expect a negative result?
bNeg := ((a < 0) xor (b < 0));
// Get the real result
Result := a * b;
// If the result is wrong, raise an error
if ((Result < 0) xor bNeg) then begin
// Raise EOverFlow
end;
end;
This bug has been fixed in RAD Studio 10.2 Tokyo.
The issue can be found here (but one have to log in with embarcadero account to see it).
Here is correct version of __llmulo by John O'Harrow (licensed under MPL 1.1) shipped with Delphi versions 10.2 and above:
// Param 1(edx:eax), Param 2([esp+8]:[esp+4])
// Result is stored in edx:eax
// O-flag set on exit => result is invalid
// O-flag clear on exit => result is valid
procedure __llmulo();
asm
test edx, edx {Param1-Hi = 0?}
jne ##Large {No, More than one multiply may be needed}
cmp edx, [esp+8] {Param2-Hi = 0?}
jne ##Large {No, More than one multiply may be needed}
mul dword ptr [esp+4] {Only one multiply needed, Set Result}
and eax, eax {Clear Overflow Flag}
ret 8
##Large:
sub esp, 28 {allocate local storage}
mov [esp], ebx {save used registers}
mov [esp+4], esi
mov [esp+8], edi
mov [esp+12], ebp
mov ebx, [esp+32] {Param2-Lo}
mov ecx, [esp+36] {Param2-Hi}
mov esi, edx
mov edi, ecx
sar esi, 31
sar edi, 31
xor eax, esi
xor edx, esi
sub eax, esi
sbb edx, esi {edx:eax (a1:a0) = abs(Param1)}
xor ebx, edi
xor ecx, edi
sub ebx, edi
sbb ecx, edi {ecx:ebx (b1:b0) = abs(Param2)}
xor esi, edi {Sign Flag, 0 if Params have same sign else -1}
mov [esp+16], eax {a0}
mov [esp+20], edx {a1}
mov [esp+24], ecx {b1}
mul ebx {edx:eax (c1:c0) = a0*b0}
xchg ebx, edx {ebx = c1, edx = b0}
mov edi, eax {abs(Result-Lo) = c0}
xor ecx, ecx {Upper 32 bits of 128 bit result}
xor ebp, ebp {Second 32 bits of 128 bit result}
mov eax, [esp+20] {a1}
mul edx {edx:eax (d1:d0) = a1*b0}
add ebx, eax {c1 + d0}
adc ebp, edx {d1 + carry}
adc ecx, 0 {Possible carry into Upper 32 bits}
mov eax, [esp+16] {a0}
mov edx, [esp+24] {b1}
mul edx {edx:eax (e1:e0) = a0*b1}
add ebx, eax {abs(Result-Hi) = c1 + d0 + e0}
adc ebp, edx {d1 + e1 + carry}
adc ecx, 0 {Possible carry into Upper 32 bits}
mov eax, [esp+20] {a1}
mov edx, [esp+24] {b1}
mul edx {edx:eax (f1:f0) = a1*b1}
add ebp, eax {d1 + e1 + f0 + carry}
adc ecx, edx {f1 + carry}
or ecx, ebp {Overflow if ecx <> 0 or ebp <> 0}
jnz ##Overflow
mov edx, ebx {Set abs(Result-Hi)}
mov eax, edi {Set abs(Result-Lo)}
cmp edx, $80000000
jae ##CheckRange {Possible Overflow if edx>=$80000000}
##SetSign:
xor eax, esi {Correct Sign of Result}
xor edx, esi
sub eax, esi
sbb edx, esi
mov ebx, [esp] {restore used registers}
mov esi, [esp+4]
mov edi, [esp+8]
mov ebp, [esp+12]
add esp, 28 {Clears Overflow flag}
ret 8
##CheckRange:
jne ##Overflow {Overflow if edx>$80000000}
test esi, esi {edx=$80000000, Is Sign Flag=0?}
jnz ##SetSign {No, Result is Ok (-MaxInt64)}
##Overflow:
mov ebx, [esp] {restore used registers}
mov esi, [esp+4]
mov edi, [esp+8]
mov ebp, [esp+12]
add esp, 28
mov ecx, $80000000
dec ecx {Set Overflow Flag}
ret 8
end;
I written an asm function in Delphi 7 but it transforms my code to something else:
function f(x: Cardinal): Cardinal; register;
label err;
asm
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz err
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
err:
xor eax, eax
end;
// compiled version
f:
push ebx // !!!
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz +$0e
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
err:
xor eax, eax
mov eax, ebx // !!!
pop ebx // !!!
ret
// the almost equivalent without asm
function f(x: Cardinal): Cardinal;
var
c: Cardinal;
begin
x := not x;
x := x and x shr 1;
if x <> 0 then
begin
c := bsf(x); // bitscanforward
x := 1 shl c;
Result := x or (x shl 1)
end
else
Result := 0;
end;
Why does it generate push ebx and pop ebx? And why does it do mov eax, ebx?
It seems that it generates the partial stack frame because of the mov eax, ebx.
This simple test generates mov eax, edx but doesn't generate that stack frame:
function asmtest(x: Cardinal): Cardinal; register;
label err;
asm
not eax
and eax, 1
jz err
ret
err:
xor eax, eax
end;
// compiled
asmtest:
not eax
and eax, $01
jz +$01
ret
xor eax, eax
mov eax, edx // !!!
ret
It seems that it has something to do with the label err. If I remove that I don't get the mov eax, * part.
Why does this happen?
Made a bug report on Quality Central.
The practical advice is: do not use label keyword in asm code, use ##-prefixed labels:
function f(x: Cardinal): Cardinal; register;
asm
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz ##err
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
##err:
xor eax, eax
end;
Updated:
I have not found the bug report in Basm area. It looks like a bug, but I have used BASM for many years and never thought about using label keyword such a way. In fact I never used label keyword in Delphi at all. :)
Well ... back then, in the Delphi-Manual, it used to say something about Compiler-Optimization and thealike-crazyness:
The Compiler generates Stackframes only for nested Routines, for Routines having local Variables and for Routines with Stack-Parameters
The auto-generated Initialization- and Finalizationcode for Routines includes:
PUSH EBP ; If Locals <> 0 or Params <> 0
MOV EBP,ESP ; If Locals <> 0 or Params <> 0
SUB ESP,Locals ; If Locals <> 0
...
MOV ESP,EBP ; If Locals <> 0
POP EBP ; If Locals <> 0 or Params <> 0
RET Params ; Always
If local Variables contain Variants, long Strings or Interfaces they are initialized with Null but aren't finalized afterwards.
Locals is the Size of local Variables, Params the Size of Parameters. If both Locals as well as Params are Null no Init-Code will be generated and the Finalizationcode only contains a RET-Intruction.
Maybe that has got something to do with it all...