News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Wrong result using invoke wspintf

Started by quocsan, March 18, 2025, 10:56:03 PM

Previous topic - Next topic

quocsan

Quote from: NoCforMe on March 19, 2025, 12:11:07 PMSo what is your problem, exactly?





As you can see in the attached image: The resulted string after calling wspintf was not as expected. 6 numbers from after "Last updated: " were all wrong.
At that moment, I was in a hurry, so I had to fix the problem by preparing the call to wspintf by hand, instead of using invoke. That's all.

NoCforMe

I think I found your problem:
Format for printing the date:
DB "Last updated: %02d/%02d/%04d"

Source of the date data:
DbfHeader struct
    m_ID            DB ?
    m_UpdatedYY     DB ? ; YYMMDD
    m_UpdatedMM     DB ?
    m_UpdatedDD     DB ?

Do you see the problem? The first two fields you're trying to use are defined in the structure as bytes (Header.m_UpdatedDD and Header.m_UpdatedMM); however, wsprintf() is using signed DWORD fields (%02d) to format this data, so it's actually using DWORDs for each field instead of just the one byte.

You can't do this kind of conversion (from a byte to a DWORD) with wsprintf(). You need to extract these two fields, convert them to a DWORD and then pass them as parameters (either in registers or in local variables). That should fix your problem.

You can most easily get them into a DWORD thus:
    MOVZX    EAX, Header.m_UpdatedMM          ;Load a byte and zero-extend it to a DWORD.
    MOV      localMM, EAX

then use localMM as the parameter for wsprintf().

(You should do the same thing with m_ID, since it's also a byte in the structure.
Or alternatively you could make the structure fields DWORDs and store them that way, in which case you can directly use them as parameters to pass to wsprintf().)
Assembly language programming should be fun. That's why I do it.

quocsan

Good job, NoCforMe!

I know the cause now and will avoid the same fault in future.
Thank you so much for your help!

NoCforMe

Happy to be of assistance.

Just remember to always keep in mind The Size of Things, since assembly language doesn't have all the guard rails that C or C++ does.
Assembly language programming should be fun. That's why I do it.

sinsi

Which version of ML are you using? Some early versions had trouble passing bytes as arguments.
I built the code with the latest ML (14.42.34438.0) and it seemed to give proper values, except the year was 0001.

quocsan

Microsoft (R) Macro Assembler Version 6.14.8444.

Now I'm editing my source code as NoCforMe guide.

NoCforMe

Quote from: sinsi on March 19, 2025, 01:49:40 PMWhich version of ML are you using? Some early versions had trouble passing bytes as arguments.
I built the code with the latest ML (14.42.34438.0) and it seemed to give proper values, except the year was 0001.
So how does wsprintf()* handle byte arguments? does it "promote" them automagically?

I thought not, but who knows?

Post some executable code? I'm curious.

* or any other function for that matter
Assembly language programming should be fun. That's why I do it.

quocsan

The result is perfect!
.if (eax != 0 && lBytesIO == sizeof Header)
invoke FlushFileBuffers, hFile
movzx edx, Header.m_UpdatedYY ; year of updated date
.if (dl < 80)
add dx, 2000
.else
add dx, 1900
.endif
movzx ecx, Header.m_UpdatedMM
movzx ebx, Header.m_UpdatedDD
movzx eax, Header.m_ID
movzx edi, Header.m_HeaderSize
movzx esi, Header.m_RecordSize
invoke wsprintf, \
addr szBuffer, \
addr szFmt, \
addr fname, \
FileSize, \
eax, \
DbfDesc, \
ebx, ecx, edx, \
Header.m_RecordCount, \
edi, \
esi
mov szMsg, OFFSET szBuffer ; to show detailed information.
.endif

sinsi

Comparison
Macro Assembler Version 6.14.8444     Macro Assembler Version 14.42.34438.0
push    0                             push    small 0
push    small [word_40388A]           push    small [word_40388A]
push    0                             push    small 0
push    small [word_403888]           push    small [word_403888]
push    dword_403884                  push    dword_403884
push    edx                           push    edx
push    0                             push    small 0
mov     al, byte_403882               mov     al, byte_403882
movzx   ax, al                        movzx   ax, al
push    ax                            push    ax
push    0                             push    small 0
mov     al, byte_403883               mov     al, byte_403883
movzx   ax, al                        movzx   ax, al
push    ax                            push    ax
push    0                             push    small 0
mov     al, byte_403880               mov     al, byte_403880
movzx   ax, al                        movzx   ax, al
push    ax                            push    ax
push    dword_4038AC                  push    dword_4038AC
push    offset FileName               push    offset FileName
push    offset aFileNameSFileS        push    offset aFileNameSFileS
push    offset stru_403634.pvReserved push    offset stru_403634.pvReserved
call    wsprintfA                     call    wsprintfA
add     esp, 2Ch                      add     esp, 2Ch
Notice push 0 and push small 0?
ML6  pushes a 32-bit 0 then a word -> 48 bits
ML14 pushes a 16-bit 0 then a word -> 32 bits - correct

The problem occurs anywhere that you supply a byte as an argument in 32-bit code.
Another reason to ditch ML6.

NoCforMe

Quote from: sinsi on March 19, 2025, 02:28:20 PMThe problem occurs anywhere that you supply a byte as an argument in 32-bit code.
Another reason to ditch ML6.
OK;
1. I guess it never occurred to me to even try that (use a byte argument in the first place with the hope that "someone" would "take care of it"). So no reason here not to use the old stuff.

2. Never saw the qualifier "small" before: means byte? word?
Assembly language programming should be fun. That's why I do it.

NoCforMe

Quote from: quocsan on March 19, 2025, 02:25:35 PMThe result is perfect!
.if (eax != 0 && lBytesIO == sizeof Header)
    invoke FlushFileBuffers, hFile
    movzx edx, Header.m_UpdatedYY ; year of updated date
    .if (dl < 80)
        add dx, 2000
    .else
        add dx, 1900
    .endif
    movzx ecx, Header.m_UpdatedMM
    movzx ebx, Header.m_UpdatedDD
    movzx eax, Header.m_ID
    movzx edi, Header.m_HeaderSize
    movzx esi, Header.m_RecordSize
    invoke wsprintf, \
        addr szBuffer, \
        addr szFmt, \
        addr fname, \
        FileSize, \
        eax, \
        DbfDesc, \
        ebx, ecx, edx, \
        Header.m_RecordCount, \
        edi, \
        esi
    mov szMsg, OFFSET szBuffer ; to show detailed information.
.endif
Good!

Since you're using non-volatile (i.e., "sacred") registers there--EBX, ESI, EDI--be sure to save and restore them around this code so they don't get trashed. (Probably not necessary if you're doing this in your "WinMain" code, or any top-level code in a Win32 program, but should always be done elsewhere.)
Assembly language programming should be fun. That's why I do it.

quocsan

Quote from: NoCforMe on March 19, 2025, 02:42:20 PMGood!

Since you're using non-volatile (i.e., "sacred") registers there--EBX, ESI, EDI--be sure to save and restore them around this code so they don't get trashed. (Probably not necessary if you're doing this in your "WinMain" code, or any top-level code in a Win32 program, but should always be done elsewhere.)
Yes, I did some changes in the source code, including preserving registers.
ProcessDBF Proc uses ebx edi esi

TimoVJL

#27
    mov RecCount, eax ; = (FileSize - Header.m_HeaderSize) / Header.m_RecordSize
    .if (eax != Header.m_RecordCount) ; Different in record counts!
stupid question, what this "fix" means ?
DBF-file can have also deleted records, so actual file size can be bigger ?

EDIT: 'Number of records in the database file' can also include deleted records.
Most DBF writers just add new record end of file and don't touch deleted records.

EDIT:
Xbase Data file (*.dbf)

QuoteEnd-of-file
dBASE II regards any End-of-File 1Ah value as the end of the file. dBASE III regard an End-of-File as an ordinary character, however it appends an extra End-of-File character at the physical end of the file.
If the file is packed the physical size of the file may be larger than the logical i.e. there may be garbage after the EOF mark.
May the source be with you

quocsan

Quote from: TimoVJL on March 19, 2025, 05:01:43 PM    mov RecCount, eax ; = (FileSize - Header.m_HeaderSize) / Header.m_RecordSize
    .if (eax != Header.m_RecordCount) ; Different in record counts!
stupid question, what this "fix" means ?
DBF-file can have also deleted records, so actual file size can be bigger ?

A DBF file is corrupted when something in its header is wrong. According to my study, deleted records are marked with asterisks ('*') at the beginning of record data, and other details in its header remain as usual.

The fix I've done is: Updating the header with correct number of records.

jj2007

Compliments, quocsan - you are a new member but apparently not a newbie :thumbsup: