I need to read a bunch of strings from command line in assembly, however only one call to scanf seems to take place. I'm pretty sure the problem is related to the scanf format. If I replace what I've currently got with a simple "%s" it works fine, thing is I have to read whole lines including spaces, so "%s" won't do. "%[^c]s" should keep reading characters until it encounters c, but there's 2 problems with this:
I actually have to input the characters \ and n for the scanning to stop.
As I said, it only reads one string. After that, all the prompts for the remaining strings get instantly printed at the program ends.
I've seen that people usually suggest using fread, however I'm not really sure how to access stdin in assembly.
Here's my code:
bits 32
global start
extern exit, scanf, printf
import exit msvcrt.dll
import scanf msvcrt.dll
import printf msvcrt.dll
segment data use32 class=data
msg DB "Enter a value for n", 13, 10, 0
s_msg DB "Enter a string", 13, 10, 0
n_format DB "%d", 0
s_format DB "%[^\n]s", 0
segment bss use32 class=bss
n DD 0
s RESB 1000
segment code use32 class=code
start:
push dword msg
call [printf]
add ESP, 4 * 1
push dword n
push dword n_format
call [scanf]
add ESP, 4 * 2
mov ECX, [n]
read_strings:
pushad
push dword s_msg
call [printf]
add ESP, 4 * 1
push dword s
push dword s_format
call [scanf]
add ESP, 4 * 2
popad
loop read_strings
push dword 0
call [exit]
Related
I have a code that has 3 procedures, one to get an input from the user, one to display a multiplication result, and lastly one for an error message. I am trying to implement the PUSH and POP operations and get my code in to the stack. It will seem long but it makes sense to me, here it is...
.data
line BYTE "The answer is ",0
line2 BYTE "Enter a number",0
kline3 BYTE Wrong.",0
int SWORD ?
.code
Val PROC
call ReadInt
mov int,edx
cmp int, 10000
jl 1
jmp end
L1: cmp intVal1, -10000
jg 2
call error
jmp end
2: ret
Val ENDP
main PROC
call Val
call Val
imul val, val
exit
main ENDP
END main
All this simply does it call to get 2 inputs twice and then call to display the the multiplied result. My question is how do you implement push and pop in to here to have it all make sense?
I would assume that you need to push in the GetValue procedure to put in input in to the stack and then pop it after each call in the main procedure and maybe do the same with the display procedure?
I am just struggling to figure it out so any help would be great!
PS. This code is an asm file in visual studio 2010
Your first call to GetValue stores its result in intVal. But then your second call to GetValue also stores its result in intVal, so the first result is forever lost.
Your MultiplyAndDisplay function expects one of the operands in intVal, and the other operand in eax. So, what you need to do is push [intVal] after the first call to GetValue, and pop eax after the second call to GetValue.
Note that the square brackets in push [intVal] are in some notation that actually makes sense, but if I remember correctly the microsoft assembler does not support that notation which actually makes sense, so you might have to code push intVal instead, or push dword ptr intVal, or something nonsensical like that to get it to work.
Because your question is tagged MASM, this is a MASM answer:
Your code can be restructured in a way that uses the MASM directive PROC with parameters and the INVOKE directive for parameter passing:
MultiplyAndDisplay PROC val1: SDWORD, val2: SDWORD
mov eax, val1
imul eax, val2 ; signed multiply of val1 by val2
mov edx, OFFSET prompt
call WriteString ; Writes the prompt in edx
call WriteDec ; Writes the value in eax
ret
MultiplyAndDisplay ENDP
main PROC
call GetValue
push [intVal] ; PUSH firstParam to the stack
call GetValue
pop eax ; POP previous param/intVal to EAX
invoke MultiplyAndDisplay, eax, intVal ; MultiplyAndDisplay, firstParam(EAX), secondParam(intVal)
exit
main ENDP
Supposing we have got a text file sample.txt:
one
two
...
Now we want to remove the first line:
two
...
A quick way to do that is to use input redirection, set /P and findstr1 (I know there are other ways using more or for /F, but let us forget about them for now):
#echo off
< "sample.txt" (
set /P =""
findstr "^"
)
The output is going to be as expected.
However, why is the output empty when I replace the input redirection < by type and a pipe | :
#echo off
type "sample.txt" | (
set /P =""
findstr "^"
)
When I replace set /P ="" by pause > nul, the output is what I expect -- the input file is output but with the first character of the first line missing (as it is consumed by pause). But why does set /P seem to consume everything instead of only the first line like it does with the redirection < approach? Is that a bug?
To me it looks like set /P fails to adequately initialise the reading pointer to the piped data.
I watched that strange behaviour on Windows 7 and on Windows 10.
It becomes even more weird: when calling the script containing the pipe multiple times, for instance by a loop like for /L %I in (1,1,1000) do #pipe.bat, and the input file contains about fifteen lines or more, sometimes (a few times out of thousand) a fragment of the input file is returned; that fragment is exactly the same each time; it seems that there are always 80 bytes missing at the beginning.
1) findstr hangs in case the last line is not terminated by a line-break, so let us assume such is there.
When retrieving data, the set /p tries to fill a 1023 character buffer (if they are available) with data from stdin. Once this read operation has ended, the first end of line is searched and once it has been found (or the end of the buffer has been reached), the SetFilePointer API is called to reposition the input stream pointer after the end of the read line. This way the next read operation will start to retreive data after the read line.
This works flawlessly when a disk file is associated with the input stream, but as Microsoft states in the SetFilePointer documentation
The hFile parameter must refer to a file stored on a seeking device;
for example, a disk volume. Calling the SetFilePointer function with a
handle to a non-seeking device such as a pipe or a communications
device is not supported, even though the SetFilePointer function may
not return an error. The behavior of the SetFilePointer function in
this case is undefined.
What is happening is that, while not generating any error, the call to reposition the read pointer fails when stdin is associated with a pipe, the pointer is not moved back and the 1023 bytes (or the number of available read bytes) keep read.
edited in response to Aacini request
The set command is processed by the eSet function, who calls SetWork to determine which type of set command will be executed.
As it is a set /p the SetPromptUser function is called and from this function the ReadBufFromInput function is called
add esp, 0Ch
lea eax, [ebp+var_80C]
push eax ; int
push 3FFh ; int
lea eax, [ebp+Value]
push eax ; int
xor esi, esi
push 0FFFFFFF6h ; nStdHandle
mov word ptr [ebp+Value], si
call edi ; GetStdHandle(x) ; GetStdHandle(x)
push eax ; hFile
call _ReadBufFromInput#16 ; ReadBufFromInput(x,x,x,x)
it requests 3FFh (1023) characters from standard input handle (0FFFFFFF6h = -10 = STD_INPUT_HANDLE)
ReadBufFromInput uses the GetFileType API to determine if it should read from the console or from a file
; Attributes: bp-based frame
; int __stdcall ReadBufFromInput(HANDLE hFile, int, int, int)
_ReadBufFromInput#16 proc near
hFile= dword ptr 8
; FUNCTION CHUNK AT .text:4AD10D3D SIZE 00000006 BYTES
mov edi, edi
push ebp
mov ebp, esp
push [ebp+hFile] ; hFile
call ds:__imp__GetFileType#4 ; GetFileType(x)
and eax, 0FFFF7FFFh
cmp eax, 2
jz loc_4AD10D3D
and, as in this case it is a pipe (GetFileType returns 3) the code jumps to the ReadBufFromFile function
; Attributes: bp-based frame
; int __stdcall ReadBufFromFile(HANDLE hFile, LPWSTR lpWideCharStr, DWORD cchWideChar, LPDWORD lpNumberOfBytesRead)
_ReadBufFromFile#16 proc near
var_C= dword ptr -0Ch
cchMultiByte= dword ptr -8
NumberOfBytesRead= dword ptr -4
hFile= dword ptr 8
lpWideCharStr= dword ptr 0Ch
cchWideChar= dword ptr 10h
lpNumberOfBytesRead= dword ptr 14h
This function will call the ReadFile API function to retrive the indicated number of characters
push ebx ; lpOverlapped
push [ebp+lpNumberOfBytesRead] ; lpNumberOfBytesRead
mov [ebp+var_C], eax
push [ebp+cchWideChar] ; nNumberOfBytesToRead
push edi ; lpBuffer
push [ebp+hFile] ; hFile
call ds:__imp__ReadFile#20 ; ReadFile(x,x,x,x,x)
The returned buffer is iterated in search of an end of line, and once it is found, the pointer in the input stream is moved after the found poisition
.text:4AD06A15 loc_4AD06A15:
.text:4AD06A15 cmp [ebp+NumberOfBytesRead], 3
.text:4AD06A19 jl short loc_4AD06A2D
.text:4AD06A1B mov al, [esi]
.text:4AD06A1D cmp al, 0Ah
.text:4AD06A1F jz loc_4AD06BCF
.text:4AD06A25
.text:4AD06A25 loc_4AD06A25:
.text:4AD06A25 cmp al, 0Dh
.text:4AD06A27 jz loc_4AD06D14
.text:4AD06A2D
.text:4AD06A2D loc_4AD06A2D:
.text:4AD06A2D movzx eax, byte ptr [esi]
.text:4AD06A30 cmp byte ptr _DbcsLeadCharTable[eax], bl
.text:4AD06A36 jnz loc_4AD12018
.text:4AD06A3C dec [ebp+NumberOfBytesRead]
.text:4AD06A3F inc esi
.text:4AD06A40
.text:4AD06A40 loc_4AD06A40:
.text:4AD06A40 cmp [ebp+NumberOfBytesRead], ebx
.text:4AD06A43 jg short loc_4AD06A15
.text:4AD06BCF loc_4AD06BCF:
.text:4AD06BCF cmp byte ptr [esi+1], 0Dh
.text:4AD06BD3 jnz loc_4AD06A25
.text:4AD06BD9 jmp loc_4AD06D1E
.text:4AD06D14 loc_4AD06D14:
.text:4AD06D14 cmp byte ptr [esi+1], 0Ah
.text:4AD06D18 jnz loc_4AD06A2D
.text:4AD06D1E
.text:4AD06D1E loc_4AD06D1E:
.text:4AD06D1E mov eax, [ebp+var_C]
.text:4AD06D21 mov [esi+2], bl
.text:4AD06D24 sub esi, edi
.text:4AD06D26 inc esi
.text:4AD06D27 inc esi
.text:4AD06D28 push ebx ; dwMoveMethod
.text:4AD06D29 push ebx ; lpDistanceToMoveHigh
.text:4AD06D2A mov [ebp+cchMultiByte], esi
.text:4AD06D2D add esi, eax
.text:4AD06D2F push esi ; lDistanceToMove
.text:4AD06D30 push [ebp+hFile] ; hFile
.text:4AD06D33 call ds:__imp__SetFilePointer#16 ; SetFilePointer(x,x,x,x)
The short summarize of a long discussion at dostips (mentioned already by Aacini
set /p problems with pipes).
Reading with set /p from a redirect, reads always to the end of the line and removes the \r\n characters.
Reading with set /p from a pipe, reads up to 1023 bytes from the pipe buffer.
It doesn't stop at any \r or \n characters but it drops all content after a \n.
After closing the pipe on the left side, a set /p on the right side will read empty lines.
friends, I need help regarding Harddisk handling using 32-bit system call. I am new to assembly language and I am using MASM assembler. I have tried some example codes but they are in 16-bit mode, and also i gives me error, while compling. I want to write in 32-bit. and i also read in a book that 32-bit is complicated, Is there any another procedure for 32-bit system call.
Example Code!
; This program calls INT 21h Function 7303h, to get free space information
; on a FAT-type drive volume. It displays both the volume size and free space.
; Runs under Windows 95/98/Me, but not under Windows NT/2000/XP.
; Last update: 12/11/01
INCLUDE Irvine16.inc
.data
buffer ExtGetDskFreSpcStruc <>
driveName BYTE "C:\",0
str1 BYTE "Volume size (KB): ",0
str2 BYTE "Free space (KB): ",0
str3 BYTE "Function call failed.",0dh,0ah,0
.code
main PROC
mov ax,#data
mov ds,ax
mov es,ax
mov buffer.Level,0 ; must be zero
mov di, OFFSET buffer ; ES:DI points to buffer
mov cx, SIZEOF buffer ; buffer size
mov dx, OFFSET DriveName ; ptr to drive name
mov ax, 7303h ; Get disk free space
int 21h
jc error ; Failed if CF = 1
mov dx,OFFSET str1 ; volume size
call WriteString
call CalcVolumeSize
call WriteDec
call Crlf
mov dx,OFFSET str2 ; free space
call WriteString
call CalcVolumeFree
call WriteDec
call Crlf
jmp quit
error:
mov dx,OFFSET str3
call WriteString
quit:
exit
main ENDP
;-------------------------------------------------------------------
CalcVolumeSize PROC
; Calculate and return the disk volume size, in kilobytes.
; Receives: buffer variable, a ExtGetDskFreSpcStruc structure
; Returns: EAX = volume size
; Remarks: (SectorsPerCluster * 512 * TotalClusters) / 1024
;--------------------------------------------------------------------
mov eax,buffer.SectorsPerCluster
shl eax,9 ; mult by 512
mul buffer.TotalClusters
mov ebx,1024
div ebx ; return kilobytes
ret
CalcVolumeSize ENDP
;-------------------------------------------------------------------
CalcVolumeFree PROC
; Calculate and return the number of available kilobytes on the
; given volume.
; Receives: buffer variable, a ExtGetDskFreSpcStruc structure
; Returns: EAX = available space, in kilobytes
; Remarks: (SectorsPerCluster * 512 * AvailableClusters) / 1024
;--------------------------------------------------------------------
mov eax,buffer.SectorsPerCluster
shl eax,9 ; mult by 512
mul buffer.AvailableClusters
mov ebx,1024
div ebx ; return kilobytes
ret
CalcVolumeFree ENDP
END main
Trying to assemble this program...
also i gives me error
Firstly, it cannot assemble because the struct ExtGetDskFreSpcStruc in the line
buffer ExtGetDskFreSpcStruc <>
is not defined before - not in this file and not in (the only) include file(s), in this case "Irvine16.inc", which doesn't contain the definition. Therefore all attempted accesses to "buffer.xxx" will throw errors.
Secondly, if you are trying to assemble this program for 32-bit, you are including a 16-bit include file in a 32-bit assembly file. You should use the 32-bit version "Irvine32.inc", because otherwise all indirect addressing like mov al, [si](16-bit) in the subroutines will fail.
Thirdly, you are calling DOS-legacy-interrupts, which are available only in Win95/98/Me, because their kernels are based on DOS. The newer NT kernel in Win2000 and above does not provide them, because it was a different OS architecture/codebase without/with reduced legacy code. The DOS-interrupt(21h) is 16-bit, because DOS was 16-bit. I don't know if there are implemented 32-bit versions of old DOS-interrupts in Win95/98/Me. A quick search yielded no indications of this having been realized. The BIOS interrupts used in "Irvine16.inc" are surely 16-bit and therefore unusable(without very complicated workarounds) in a 32-bit executable probably crashing the program.
Is there any another procedure for 32-bit system call[?]
Yes. Use the Win32-API, which is available in Win95 till Win8/10(?).
The easiest way to do this would be using the MASM-package by Hutch found at MOVSD.COM and then include kernel32.inc/lib from it. Then you can call the GetFreeDiskSpace function to get the desired result with a line similar to this:
invoke GetFreeDiskSpace,
offset driveName,
offset buffer.lpSectorsPerCluster, ; adjust to actual buffer layout names
offset buffer.lpBytesPerSector, ; "
offset buffer.lpNumberOfFreeClusters, ; "
offset buffer.lpTotalNumberOfClusters ; "
See full code here.
I have filled a buffer (malloc'd) with an fread call and it is a success. I am now trying to iterate over the buffer and commence parsing the input. I'm trying to start really simple by walking the buffer and output each char to the screen. But my loop is just outputting the entire input. Here is the loop portion of the code:
mov ecx, 0
mov ebx, buffer
.readByte:
push DWORD [ebx + 1 * ecx]
push DWORD ecx
push DWORD char
call _printf
add esp, 12
incr ecx
cmp ecx, [fsz]
jge .endRead
jmp .readByte
The contents of the source file that is read in (s1.txt) is:
1 + 2;
My goal is to simply output:
1
+
2
;
Since you used %s format, which indicates a string, and that without a length specifier, why did you expect it to print just a single character? You should try %c format and something like movzx eax, byte [ebx + ecx]; push eax to pass the argument. A %.1s format specifier could also work and then you can keep your argument passing. Don't forget to add a newline too, if you want that. You could also just use putchar of course.
Oh, and ecx is a caller-saved register, as such any function you call may destroy its value. So if you want to keep using that, you need to save and restore it yourself.
I'm a newbie to asm and trying to make a simple hello world which awaits for the user to press a key to end. For now the hello world is all good, but the .exe console program i got from this just close instantly while i want it to stay on screen untill the user press a key.
Now the problem i have is that for some reason, the program keep looping, searching for user input, but when i force close the program (^C) i can see all the keys i pressed are written on the next console line, like if it was using the wrong buffer (?)
I've been searching a fix all over the internet for a few days and finally I'm asking for help cuz this is driving me crazy ^^
Everything i found is mostly based on int system or under linux, while i have to deal with the windows api...
Thank you very much, any help or hint is welcome!
Code :
STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10
NULL equ 0
global start
extern ExitProcess, GetStdHandle, WriteConsoleA, ReadConsoleInputA
section .data
msg db "Hello World!", 13, 10, 0
msg.len equ $ - msg
consoleInHandle dd 1
section .bss
buffer resd 2
buffer2 resd 2
section .text
start:
push STD_OUTPUT_HANDLE
call GetStdHandle
push NULL
push buffer
push msg.len
push msg
push eax
call WriteConsoleA
read:
push STD_INPUT_HANDLE
call GetStdHandle
mov [consoleInHandle],eax
push consoleInHandle
push dword[buffer2]
push 1
push NULL
call ReadConsoleInputA
cmp eax,1
jge exit
jmp read
exit:
push NULL
call ExitProcess
Moar info about windows functions can be found here:
ReadConsoleInput
WriteConsole
push consoleInHandle pushes the address, not the handle. You want push dword [consoleInHandle]. Conversely, for the buffer you want to pass the address, so you need push buffer2 there. Also, this buffer should be the size of an INPUT_RECORD structure, which I believe is 32 bytes.
Update: As Frank commented, the argument order was also wrong.
This code works for me (note I had to add the #xx stdcall decorations due to how my environment is set up - apparently you don't need those):
STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10
NULL equ 0
global start
extern ExitProcess#4, GetStdHandle#4, WriteConsoleA#20, ReadConsoleInputA#16
section .data
msg db "Hello World!", 13, 10, 0
msg.len equ $ - msg
consoleInHandle dd 1
section .bss
buffer resd 2
buffer2 resb 32
section .text
start:
push STD_OUTPUT_HANDLE
call GetStdHandle#4
push NULL
push buffer
push msg.len
push msg
push eax
call WriteConsoleA#20
read:
push STD_INPUT_HANDLE
call GetStdHandle#4
mov [consoleInHandle],eax
push NULL
push 1
push buffer2
push dword [consoleInHandle]
call ReadConsoleInputA#16
exit:
push NULL
call ExitProcess#4