News:

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

Main Menu

Win32 Console Addition

Started by demondoido, April 09, 2013, 01:56:04 AM

Previous topic - Next topic

demondoido

I'm searching and I can not find any similar case, this is getting weirder........  :icon_confused:

qWord

it maybe simpler for you to use the C runtime library (CRT) for console IO, instead of that raw input method.
include \masm32\include\masm32rt.inc
.const
    frmt1 CHAR "%d",0
    sz0 CHAR "calculates C = A+B whereas A, B and C are signed integers",13,10
            CHAR "A = ",0
    sz1 CHAR "B = ",0
    frmt2 CHAR "C = %d",13,10,0
.code
main proc
LOCAL value1:SDWORD,value2:SDWORD
   
    invoke crt_printf,OFFSET sz0
    invoke crt_scanf,OFFSET frmt1,ADDR value1
    invoke crt_printf,OFFSET sz1
    invoke crt_scanf,OFFSET frmt1,ADDR value2
    mov eax,value1
    add eax,value2
    invoke crt_printf,OFFSET frmt2,eax
   
    inkey
    exit
   
main endp
end main

Remarks that the CRT functions are prefixed by "crt_" in the MASM32 package.
MREAL macros - when you need floating point arithmetic while assembling!

demondoido

Yeap, but I want to use direct the Win32 API :S

demondoido

I still look for a solution, but it's complicated.   :(

qWord

Quote from: demondoido on April 09, 2013, 08:35:45 AM
I still look for a solution, but it's complicated.   :(
You should move your focus on assembler programming and not waste your time by implementing boring console IO by hand.

;__UNICODE__ EQU 1
include \masm32\include\masm32rt.inc
TCHR macro lbl,args:VARARG
    IFDEF __UNICODE__
        UCSTR lbl,args
    ELSE
        lbl CHAR args
    ENDIF
endm

.const
    TCHR szNewLine,13,10,0
.code

;return: SDWORD
tchr2sdword proc uses esi pszBuffer: ptr TCHAR
LOCAL bSigned:BOOL
   
    mov bSigned,FALSE
    mov esi,pszBuffer
    xor ecx,ecx
    xor eax,eax
    movzx edx,TCHAR ptr [esi]
    .if edx == '-'
        mov bSigned,TRUE
        inc ecx
    .elseif edx == '+'
        inc ecx
    .endif
    .while 1
        movzx edx,TCHAR ptr [esi+ecx*TCHAR]
        .break .if edx != 20h && edx != 9
        inc ecx
    .endw
    .while 1
        movzx edx,TCHAR ptr [esi+ecx*TCHAR]
        .break .if !edx || ecx > 10
        imul eax,10
        lea eax,[eax+edx-'0']
        inc ecx
    .endw
    .if bSigned
        neg eax
    .endif
    ret
   
tchr2sdword endp

; return: BOOL
readLine proc pszBuffer: ptr TCHAR,cc:DWORD
LOCAL hStdIn:HANDLE
LOCAL NumberOfCharsRead:DWORD
   
    mov hStdIn,rv(GetStdHandle,STD_INPUT_HANDLE)
    invoke SetConsoleMode,hStdIn,ENABLE_LINE_INPUT or ENABLE_ECHO_INPUT or ENABLE_PROCESSED_INPUT
    .if rv(ReadConsole,hStdIn,pszBuffer,cc,ADDR NumberOfCharsRead,0) && NumberOfCharsRead
        mov edx,pszBuffer
        xor ecx,ecx
        .while ecx < NumberOfCharsRead
            .if TCHAR ptr [edx+ecx*TCHAR] == 13
                mov TCHAR ptr [edx+ecx*TCHAR],0
                .break
            .endif
            inc ecx
        .endw
        mov eax,TRUE
    .else
        xor eax,eax
    .endif
    ret
   
readLine endp

; return: BOOL
printTStr proc pszTStr:ptr TCHAR,bCRLF:BOOL
LOCAL hStdOut:HANDLE
LOCAL NumberOfCharsWrite:DWORD

    mov hStdOut,rv(GetStdHandle,STD_OUTPUT_HANDLE)
    invoke SetConsoleMode,hStdOut,ENABLE_PROCESSED_OUTPUT or ENABLE_WRAP_AT_EOL_OUTPUT
    mov edx,rv(lstrlen,pszTStr)
    invoke WriteConsole,hStdOut,pszTStr,edx,ADDR NumberOfCharsWrite,0
    .if bCRLF
        invoke WriteConsole,hStdOut,ADDR szNewLine,2,ADDR NumberOfCharsWrite,0
    .endif
    ret
   
printTStr endp


main proc
LOCAL value1:SDWORD
LOCAL sz[256]:TCHAR

    .repeat
       
        fn printTStr,"A = ",FALSE
        .break .if !rv(readLine,ADDR sz,LENGTHOF sz)
       
        invoke tchr2sdword,ADDR sz
        mov value1,eax
       
        fn printTStr,"B = ",FALSE
        .break .if !rv(readLine,ADDR sz,LENGTHOF sz)
       
        invoke tchr2sdword,ADDR sz
        add eax,value1
        fn wsprintf,ADDR sz,"C = %d",eax ; WinAPI ;-)
        invoke printTStr,ADDR sz,TRUE
       
        ;inkey
    .until 1
    invoke ExitProcess,0
   
main endp
end main
MREAL macros - when you need floating point arithmetic while assembling!

dedndave

i agree - there's no real need to have a perfect console app
the console is buggish, by nature

however, we should be able to limit line input to some specified length
it shouldn't be that difficult - lol

here is what i'm thinking
when we SetConsoleMode, turn off the ENABLE_LINE_INPUT bit
then, we use ReadFile to read individual bytes until
a) the buffer is full
b) carriage return is detected

i'll play with this some more tomorrow

dedndave

i had a little time to play today - not as much as i had hoped   :(

we are in the middle of doing repairs on my sister's house, and it rained last night
made for an interesting evening

at any rate.....
this has been an educational little experience - lol

as far as using Write/Read File/Console, we have typically used the File functions in the past
but, if you want to write UNICODE aware routines,
i would have to say that WriteConsole and ReadConsole make the transition a little smoother
they deal with character counts, whereas the File functions deal with byte counts
the masm32 stdin functions use ReadFile for ANSI and ReadConsole for UNICODE
with a little adjustment, it may be best to use ReadConsole and make a single routine for both

the problem concerning line input, where the function does not limit the character count......

this problem occurs because we normally run with the ENABLE_LINE_INPUT mode bit set
that is how we are able to use ENABLE_ECHO_INPUT
we also typically use ENABLE_PROCESSED_INPUT

QuoteENABLE_ECHO_INPUT
Characters read by the ReadFile or ReadConsole function are written to the active screen buffer
as they are read. This mode can be used only if the ENABLE_LINE_INPUT mode is also enabled.

ENABLE_LINE_INPUT
The ReadFile or ReadConsole function returns only when a carriage return character is read.
If this mode is disabled, the functions return when one or more characters are available.

ENABLE_PROCESSED_INPUT
CTRL+C is processed by the system and is not placed in the input buffer. If the input buffer is
being read by ReadFile or ReadConsole, other control keys are processed by the system and
are not returned in the ReadFile or ReadConsole buffer. If the ENABLE_LINE_INPUT mode is
also enabled, backspace, carriage return, and line feed characters are handled by the system.

it's like a domino effect of caveats - lol

if you look at the masm32 package StdIn routines,
(\Masm32\M32Lib\stdin.asm and \Masm32\M32Lib\stdinW.asm)
you will see that the input mode is set as mentioned above

what i am just now learning is that "the console mode" is actually 2 different "console modes"
you'd think i would have figured that out, long ago   :lol:
actually, i guess i knew it, but didn't realize the impact
and, i am also learning that the mode set for hStdOut affects the behaviour of hStdInp   :redface:

QuoteENABLE_PROCESSED_OUTPUT
Characters written by the WriteFile or WriteConsole function or echoed by the ReadFile or
ReadConsole function are examined for ASCII control sequences and the correct action is
performed. Backspace, tab, bell, carriage return, and line feed characters are processed.

ENABLE_WRAP_AT_EOL_OUTPUT
When writing with WriteFile or WriteConsole or echoing with ReadFile or ReadConsole,
the cursor moves to the beginning of the next row when it reaches the end of the current
row. This causes the rows displayed in the console window to scroll up automatically
when the cursor advances beyond the last row in the window. It also causes the contents
of the console screen buffer to scroll up (discarding the top row of the console screen buffer)
when the cursor advances beyond the last row in the console screen buffer. If this mode is
disabled, the last character in the row is overwritten with any subsequent characters.

more domino caveats
actually, the descriptions are incomplete and a little inaccurate
they do not mention the behaviour of the left and right arrow keys
and, the input buffer ENABLE_INSERT_MODE seems to be enabled even when the bit is cleared
also true of some of the mouse stuff

so, when we read input, we really ought to be controlling the standard output mode, as well
after playing around with the different mode combinations, i have come to the conclusion
that the best way to go is to turn off ENABLE_LINE_INPUT and ENABLE_ECHO_INPUT,
and write your own code to handle the echo and cursor control stuff
this allows you to control the line length according to buffer size (a la DOS buffered input)
a bit more code - but we get more control over the behaviour - not that bad
on the bright side, the input buffer mode doesn't seem to affect the write functions   :biggrin:

tomorrow, i should have a little time to write a set of routines

dedndave

#22
here is a UNICODE aware ConOut routine
ConOut  PROTO   :LPVOID,:DWORD

;***********************************************************************************************

ConOut  PROC USES EBX lpBuffer:LPVOID,nChars:DWORD

;UNICODE aware ConOut routine - DednDave, 4-2013

;--------------------------------------------

    LOCAL   dwOrigModeOut         :DWORD
    LOCAL   nNumberOfCharsWritten :DWORD

;--------------------------------------------

    INVOKE  GetStdHandle,STD_OUTPUT_HANDLE
    xchg    eax,ebx
    INVOKE  GetConsoleMode,ebx,addr dwOrigModeOut
    mov     edx,ENABLE_PROCESSED_OUTPUT or ENABLE_WRAP_AT_EOL_OUTPUT
    .if edx!=dwOrigModeOut
        INVOKE  SetConsoleMode,ebx,edx
    .endif
    xor     ecx,ecx
    mov     nNumberOfCharsWritten,ecx
    INVOKE  WriteConsole,ebx,lpBuffer,nChars,addr nNumberOfCharsWritten,ecx
    mov     edx,dwOrigModeOut
    .if edx!=(ENABLE_PROCESSED_OUTPUT or ENABLE_WRAP_AT_EOL_OUTPUT)
        INVOKE  SetConsoleMode,ebx,edx
    .endif
    mov     eax,nNumberOfCharsWritten
    ret

ConOut  ENDP

;***********************************************************************************************


EDIT: modified so it gets standard handle - a reasonably fast API
EDIT: corrected syntax error "or" was "||"

Gunther

Good work, Dave. Thank you.  :t

Gunther
You have to know the facts before you can distort them.

demondoido

Wow, you're amazing... Perfect!

dedndave

thanks Gunther

and thanks Doido   :P
i don't know about "amazing", though - lol

you can use that routine with INVOKE
put the prototype near the beginning of the source file
    INVOKE  ConOut,offset sString,lengthof sString

i also like to use qWord's macro to define ANSI/UNICODE strings
;tchr macro by qWord

tchr    MACRO   lbl,args:VARARG
    IFDEF __UNICODE__
        UCSTR lbl,args
    ELSE
        lbl db args
    ENDIF
        ENDM


then, before the INCLUDE line, you can define the symbol __UNICODE__
with an equate to build a UNICODE version of the program
by commenting it out, you can build the ANSI version

when i get finished with the ConInp routine, i'll post an example

Gunther

Thank you Dave. A clean and clear solution.

Gunther
You have to know the facts before you can distort them.

qWord

It should be mention that Read/WriteConsole only works with console input/output screen buffers. For pipes and files the function fails (e.g. redirection). The "correct" code is more complicated and that why we should simply use the CRT...
MREAL macros - when you need floating point arithmetic while assembling!

dedndave

that may be true for pipes
but, how often do we want a function that simply gets a string from the user ?

i actually prefer using the CRT for keyboard input, but we should be able to program around the API, too - lol

Gunther

Dave,

Quote from: dedndave on April 11, 2013, 05:40:48 AM
that may be true for pipes
but, how often do we want a function that simply gets a string from the user ?

i actually prefer using the CRT for keyboard input, but we should be able to program around the API, too - lol

I agree with you. On the other hand, qWord isn't wrong. For the beginners it's not easy to distinguish the essential from the inessential.

Gunther
You have to know the facts before you can distort them.