The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: Elvenmonk on November 18, 2011, 05:37:01 AM

Title: How to get user input
Post by: Elvenmonk on November 18, 2011, 05:37:01 AM
Hi I'm currently in an assembly class and before we were using inline MASM assembly in Visual Studios with C/C++. However, our professor recently made it so we can't use Visual Studios anymore for input and output. I've gotten output to work I just can't figure out how to do input.
Title: Re: How to get user input
Post by: hutch-- on November 18, 2011, 05:45:06 AM
This is how to do Standard Input using the Windows API functions.


; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    .386
    .model flat, stdcall
    option casemap :none   ; case sensitive

    include \masm32\include\windows.inc
    include \masm32\include\kernel32.inc

    .code

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

StdIn proc lpszBuffer:DWORD,bLen:DWORD

    LOCAL hInput :DWORD
    LOCAL bRead  :DWORD

    invoke GetStdHandle,STD_INPUT_HANDLE
    mov hInput, eax

    invoke SetConsoleMode,hInput,ENABLE_LINE_INPUT or \
                                 ENABLE_ECHO_INPUT or \
                                 ENABLE_PROCESSED_INPUT

    invoke ReadFile,hInput,lpszBuffer,bLen,ADDR bRead,NULL

  ; -------------------------------
  ; strip the CR LF from the result
  ; -------------------------------
    mov edx, lpszBuffer
    sub edx, 1
  @@:
    add edx, 1
    cmp BYTE PTR [edx], 0
    je @F
    cmp BYTE PTR [edx], 13
    jne @B
    mov BYTE PTR [edx], 0
  @@:

    mov eax, bRead
    sub eax, 2

    ret

StdIn endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Title: Re: How to get user input
Post by: dedndave on November 18, 2011, 05:47:15 AM
hi Elvan
start by installing the masm32 package
read the installation page, then install it   :U
from there, you can learn basics from the library and examples

here is the code from stdin.asm....
; #########################################################################

   .386
   .model flat, stdcall
   option casemap :none   ; case sensitive

   include \masm32\include\windows.inc
   include \masm32\include\kernel32.inc

   .code

; #########################################################################

StdIn proc lpszBuffer:DWORD,bLen:DWORD

   LOCAL hInput :DWORD
   LOCAL bRead  :DWORD

   invoke GetStdHandle,STD_INPUT_HANDLE
   mov hInput, eax

   invoke SetConsoleMode,hInput,ENABLE_LINE_INPUT or \
                                ENABLE_ECHO_INPUT or \
                                ENABLE_PROCESSED_INPUT

   invoke ReadFile,hInput,lpszBuffer,bLen,ADDR bRead,NULL

   mov eax, bRead

   ret

StdIn endp

; #########################################################################

end


as you can see, it is treated as a file stream
Title: Re: How to get user input
Post by: Elvenmonk on November 18, 2011, 05:55:56 AM
I tried to run the first code and I got these errors:
(17) error A2006:undefined symbol : STD_INPUT_HANDLE
(17) error A2114: INVOKE argument type mismatch : argument : 1
(20) error A2006: undefined symbol : ENABLE_LINE_INPUT
(20) error A2114: INVOKE argument type mismatch : argument : 2
(24) error A2006:undefined symbol : NULL
(24) error A2114: INVOKE argument type mismatch : argument : 5
(46) error A2088 END directive required at end of file

Also, I don't know how different the MASM y'all have here is but we're to use the ml.exe and linker.exe from VS 2010. Since that's what my TA and prof will be using the grade my work I can't install or change anything.
Along with this, I don't really understand much of what is being done. My professor does a bad job of explaining assembly. All I understand is basic commands, I guess is the word, like: jmp,mov,sub,add,jne,jg, and etc.

And the second code you gave me I got these errors too.
Title: Re: How to get user input
Post by: NoCforMe on November 18, 2011, 06:22:07 AM
Undefined symbol errors mean, well, that the symbol (STD_INPUT_HANDLE in this case) isn't defined, which usually means that a file which contains the definition isn't being INCLUDEDd or can't be found.

Probably best for you to do a full installation of MASM32, which will put itself in the root of the drive you select. Your (newer) versions of ML and link.exe should work (that's what MASM32 is based on, and the current versions are 6.14 for the assembler and 5.12 for the linker.

Check your file PATHs to make sure the assembler and linker can "see" the required files.
Title: Re: How to get user input
Post by: Elvenmonk on November 18, 2011, 06:31:15 AM
Well, what exactly does it need to see? If it's those included files then I have them set to their path on my computer.
Title: Re: How to get user input
Post by: MichaelW on November 18, 2011, 07:29:44 AM
The %PATH% environment variable specifies the search path for executables. Since the MASM32 package is designed to not depend on environment settings, IMO you should specify the full path for everything. That way you don't have to set up a workable environment, or otherwise have to worry about the existing environment causing the system or the tools to use an executable, include file, or library that you did not intend.

But the system, ML, and the linker can use the environment settings, and you can take advantage of this by setting up a copy of the environment with the necessary settings.

Test.asm:

;==============================================================================
    .486                       ; create 32 bit code
    .model flat, stdcall       ; 32 bit memory model
    option casemap :none       ; case sensitive

    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    include masm32.inc
    includelib masm32.lib
    include msvcrt.inc
    includelib msvcrt.lib

    include d:\masm32\macros\macros.asm
;==============================================================================

printf MACRO format:REQ, args:VARARG
    IFNB <args>
        invoke crt_printf, cfm$(format), args
    ELSE
        invoke crt_printf, cfm$(format)
    ENDIF
    EXITM <>
ENDM

;==============================================================================
    .data
        rect RECT <0,1,2,3>
    .code
;==============================================================================

testrc proc rc:RECT
    printf( "%d\t%d\t%d\t%d\n\n", rc.left, rc.top, rc.right, rc.bottom )
    ret
testrc endp

;==============================================================================
start:
;==============================================================================

    invoke testrc, rect

    inkey "Press any key to exit..."
    exit

;==============================================================================
end start

The batch file:

set file="test"
set path=d:\masm32\bin
set include=d:\masm32\include
set lib=d:\masm32\lib
ML /c /coff %file%.asm
pause
Link /SUBSYSTEM:CONSOLE %file%.obj
pause


Title: Re: How to get user input
Post by: dedndave on November 18, 2011, 01:24:20 PM
what you are missing is the windows.inc include file, et al
that is where many of those symbols are defined
the simplest way to start a program is to use one include file that adds several others
        INCLUDE \masm32\include\masm32rt.inc

take a look at that file with notepad - it will show you all the typing it saves you   :P
Title: Re: How to get user input
Post by: dedndave on November 18, 2011, 01:35:16 PM
here is a little test program.....
Title: Re: How to get user input
Post by: Elvenmonk on November 20, 2011, 05:56:13 AM
Sorry for taking so long to reply, busy with school and work.
Well, I found out how our prof. wants us to use it, but he didn't explain how to implement it. I know we have to do: call scanf.
However I don't know how to use it honestly. Here is what I was trying. I asked him after class and he said if I installed the MASM like y'all we reccomendng I would've gotten a 0 because then the TA or him wouldn't have had itso the program would not run.

the bat file is what our prof told to use when compiling it.

Dedndave trying your code right now.

I ran you code without installing the MASM32 sdk and it didn't work. Then when I installed it I got this error:
Assembling: StdInTst.asm
\masm32\include\windows.inc(10938) : error A2191:cannot include structure in sel
f
Title: Re: How to get user input
Post by: MichaelW on November 20, 2011, 07:23:47 AM
I modified your source and batch file so they do not depend on any MASM32 components other than ML and link, and obviously so they would work on my system, so you will need to correct the paths in the batch file.

; TITLE simple ML.exe test program assembled on @date at @time
;.MODEL FLAT,STDCALL

.486                  ; create 32 bit code, must precede model directive
.model flat, stdcall  ; 32 bit memory model
option casemap :none  ; case sensitive

;include C:\Users\Elvenmonk\Documents\programs\asm\msvcrt.inc
;include C:\Users\Elvenmonk\Documents\programs\asm\kernel32.inc
;includelib "C:\Program Files\Microsoft SDKs\Windows\v7.0A\Lib\kernel32.lib"
;includelib "C:\Program Files\Microsoft Visual Studio 10.0\VC\lib\msvcrt.lib"
;includelib "C:\Program Files\Microsoft Visual Studio 10.0\VC\lib\libcmt.lib"

; These prototypes make it possible to call the
; functions with invoke, but the push and call
; method will work just as well.

printf PROTO C :VARARG
scanf PROTO C :VARARG

.LISTMACROALL

;PRINTF MACRO string
;   IFB <string>
;   ECHO ***ERROR*** in macro PRINTF: String is empty
;   ;********
;   ;******
;   ;ECHO ***ERROR*** in macro PRINTF: String is empty
;   ;*****
;   ;********
;   ELSE
;     invoke printf, ADDR string
;   ENDIF
;ENDM

;SCANF  MACRO

;ENDM

.data
;hello  DB "Hello world!",10,0
;date   DB "Nothing",10,0
;charrar  DB "%s",0

xxx  dd 0
yyy  db 20 dup(0)

fmt1 db "%d",0
fmt2 db "%d%c",0
fmt3 db "%s",0
fmt4 db "%s%c",0

.code
main:

    ;PRINTF
    ;PRINTF hello
    ;PRINTF date
    ;SCANF
    ;mov dword [esp],charrar
    ;call scanf
    ;PRINTF [esp]

    invoke scanf, ADDR fmt1, ADDR xxx
    invoke printf, ADDR fmt2, xxx, 10

    invoke scanf, ADDR fmt3, ADDR xxx
    invoke printf, ADDR fmt4, ADDR xxx, 10

    ret
end  main


rem
rem  run assembler
rem

set file="test"
set path=D:\masm32\bin
set lib=C:\Program Files\Microsoft Visual C++ Toolkit 2003\lib

:echo on
del %file%.obj
del %file%.exe
del %file%.lst
del %file%.sbr
ml /c /coff /Cp /Fl /Fr  %file%.asm
pause
link %file%.obj /DEFAULTLIB:libcmt /SUBSYSTEM:CONSOLE /ENTRY:mainCRTStartup
pause
:echo off
:rem
echo.
test
pause


http://msdn.microsoft.com/en-us/library/9y6s16x1(v=vs.71).aspx

Edit: For some reason I was assuming that you wanted to input an integer. I have now modified the source to accept an integer and a string.
Title: Re: How to get user input
Post by: Elvenmonk on November 20, 2011, 07:42:24 AM
The batch file wasn't mine, it was what my prof said would be used for running our code. Trying out the code now. Also, SCANF and PRINTF are supposed to be macros in our assignment.

Actually, thank you very much. Sorry for sounding so negative, but I'm just stressed due to this work. But, thank you MichaelW.You taught me a bit and how to use this. I was so focused on call scanf (as that's what a few other students were using) I didn't bother to look into invoke scanf.

And yes it worked,as an FYI. Thank you very much.
Assembly seems like such a nice(not in the friendly way) language I just wish I had some helpful readings for learning assembly.
Title: Re: How to get user input
Post by: MichaelW on November 20, 2011, 08:36:32 AM
Printf and scanf require a format string, so in your macros you can specify the first macro argument as REQ, and the second as VARARG. When you invoke the function within the macro you can just pass the second macro argument (IFNB) as a single argument, regardless of how many arguments it actually represents.

http://webster.cs.ucr.edu/Page_TechDocs/MASMDoc/ProgrammersGuide/Chap_09.htm
Title: Re: How to get user input
Post by: dedndave on November 20, 2011, 09:18:30 AM
QuoteAlso, SCANF and PRINTF are supposed to be macros in our assignment.

these are functions in the msvcrt (microsoft visual c runtime) library:

http://msdn.microsoft.com/en-us/library/9y6s16x1.aspx
http://msdn.microsoft.com/en-us/library/6ttkkkhh.aspx
http://msdn.microsoft.com/en-us/library/xdb9w69d.aspx

http://msdn.microsoft.com/en-us/library/wc7014hz.aspx
http://msdn.microsoft.com/en-us/library/hf4y5e3w.aspx
http://msdn.microsoft.com/en-us/library/25366k66.aspx

QuoteI was so focused on call scanf (as that's what a few other students were using) I didn't bother to look into invoke scanf.
QuoteAlso, SCANF and PRINTF are supposed to be macros in our assignment.

macros are neither CALL'ed, nor INVOKE'd
however, macros may use those constructs inside them
for the sake of comparison, we have a macro in the masm32 package called "print"
it may be used in a number of ways, but a couple simple forms may look something like this
        print   offset szString
        print   chr$(13,10)


INVOKE is actually a macro, defined internally by the assembler
        INVOKE  SomeFunction,Parm1,Parm2
the assembler actually generates code that looks like this
        push    Parm2
        push    Parm1
        call    SomeFunction


QuoteHowever, our professor recently made it so we can't use Visual Studios anymore for input and output.

i am a little confused as to what the assignment requires, here   :P
it sounds to me as though he wants you to write printf and scanf macros that use the API functions
API functions for standard console output and input are part of the OS
generally, we use WriteFile and ReadFile functions to perform these operations, however there are other ways
those 2 API functions are part of kernel32

if you want a good grade...
1) find out exactly what your instructor expects
2) make your setup match his
Title: Re: How to get user input
Post by: MichaelW on November 20, 2011, 11:27:39 AM
The provided batch file specifies /DEFAULTLIB:libcmt, so I'm fairly certain that the macros were supposed to use the scanf and printf functions from the specified library, although I don't understand why a static library that supports multithreaded apps, for something like this.

I'm not sure that the use of invoke will be acceptable, but handling a VARARG function in a VARARG macro with the push call method would be somewhat difficult for a beginner, given the need to push the arguments in reverse order.
Title: Re: How to get user input
Post by: Elvenmonk on November 21, 2011, 12:43:56 AM
This is what the instructions are. I was working on STDIN macro when I came here.
Design and implement macros to do the following:
•Convert a signed BCD integer to a signed integer, allowing the programmer to determine the length of the resulting binary number (32, 16 or 8 bit). Remember that you need to handle BCD strings that are too large (or small) to convert successfully.
•Convert a signed integer (32, 16 or 8 bit) to a signed BCD integer suitable for printing.
•Print a string to STDOUT using the Standard C Library
•Read a string from STDIN using the Standard C Library

Before we were using inline assembly(with visual studios) for all I/O and variable declarations ( something else I'm still not 100% on in assembly)
currently reading all the docs you guys provided right now.
Title: Re: How to get user input
Post by: dedndave on November 21, 2011, 01:02:10 AM
that doesn't look too bad   :P

it looks like he wants you to incorporate printf and scanf into macros
however, he didn't say that the macros had to accomodate the full functionality of those functions

he seems to be misusing the term "BCD strings" - i think he means ASCII strings (e.g. "suitable for printing")
Title: Re: How to get user input
Post by: qWord on November 21, 2011, 01:07:15 AM
Quote from: dedndave on November 21, 2011, 01:02:10 AMhe seems to be misusing the term "BCD strings" - i think he means ASCII strings (e.g. "suitable for printing")
however, MASM can encode BCD numbers  (->TBYTE).
Title: Re: How to get user input
Post by: MichaelW on November 21, 2011, 05:22:51 AM
The most recent description seems to use "BCD integer" and "BCD string" interchangeably, so I'm assuming that a BCD string is what I would call a decimal string. The description does not mention scanf or printf, and apparently you are not to use these or other CRT functions to do the integer to/from decimal string conversions. I think the intended CRT functions may be  _cgets (http://msdn.microsoft.com/en-us/library/3197776x(v=VS.71).aspx) and  _cputs (http://msdn.microsoft.com/en-us/library/3w2s47z0(v=VS.71).aspx).
Title: Re: How to get user input
Post by: Elvenmonk on November 21, 2011, 06:46:11 AM
cgets and cputs?
As for the BCD we're supposed to read in 15chars of numbers plus a sign then remove the "3"s so like 12 would be "3132" and cut it down to "12".

Also, Michael when using your code I ran into an error. Whenever I would type in a string higher then 4 chars it would cut off the first 4 characters.
Edit, nevermind? It's odd when I typed it out, like you had it I was getting print errors. So I then copy your code and it worked 100%. So, I then compared it line by line with what I typed, but it was 100% exactly has you had typed. I have no idea why it was wigging out. Thank you so much.
Title: Re: How to get user input
Post by: dedndave on November 21, 2011, 09:38:33 AM
cgets - i'd use _cgets_s
Gets a character string from the console.
http://msdn.microsoft.com/en-us/library/tz00x758.aspx

although, you can use the simpler version _cgets
http://msdn.microsoft.com/en-us/library/3197776x.aspx

_cputs
Puts a string to the console.
http://msdn.microsoft.com/en-us/library/3w2s47z0.aspx

wrap them in macros and that part is done

the "hard" part is base conversion, which isn't all that hard
there are a few special cases to watch for...
8-bit signed values range from -128 to +127
16-bit signed values range from -32768 to +32767
32-bit signed values range from -2147483648 to +2147483647

so, except for the values -128, -32768, and -2147483648, you can NEGate negative values and convert them as positive
those values are special because they cannot be negated without overflow
in hex, they are 80h, 8000h, and 80000000h, respectively
Title: Re: How to get user input
Post by: dedndave on November 21, 2011, 10:03:20 AM
also...

you can use the CBW, CWD, CWDE, CDQ instructions to extend signed values in size
http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_6/CH06-1.html#HEADING1-226

this type of method may allow you to share the same code for conversions of all the required sizes