[英]“writing to a file” procedure in x86 (MASM) assembly not displaying correct data
I have a WriteFileContents procedure that was provided to me from my instructor. 我有一个由老师提供的WriteFileContents过程。 It's suppose to write data to a file, but Chinese characters are being displayed in the text file produced and not the original english text.
假定将数据写入文件,但是在生成的文本文件中显示的是中文字符,而不是原始的英文文本。 Also when I enter data using option 1 then chose to save it to file, nothing is saved in the generated file.
同样,当我使用选项1输入数据然后选择将其保存到文件时,在生成的文件中什么也没有保存。 In this snippet of my program I have 3 operations.
在我的程序的这段代码中,我有3个操作。
the data to be written to the file is stored in the variable fdata. 要写入文件的数据存储在变量fdata中。
the key procedures in play are WriteFileContents and Update_fdata I debugged several times and wasn't able to pinpoint the issue. 我正在调试的关键过程是WriteFileContents和Update_fdata ,但无法确定问题所在。 I don't know why the text is displaying incorrectly.
我不知道为什么文本显示不正确。
title prog.asm ;DOS file name of program
.586 ;enable all pentium instructions
.model flat, stdcall ;memory model & calling convention
.stack 8192 ;allocate 8k for stack
INCLUDELIB kernel32.lib ;Include the kernel 32 library
;----------------------------------------------------------
; Constant Definitions
;----------------------------------------------------------
MAX_LINE equ 80
BUF_SIZE equ 240
STD_INPUT equ -10d ;Function number for keyboard input
STD_OUTPUT equ -11d ;Function number for monitor output
LF equ 10d ;Line feed ascii constant
CR equ 13d ;Carriage return constant
NEWLINE equ CR,LF ;Combine CR and LF for carriage return
ENABLE_PROCESSED_INPUT equ 1 ;Flag to turn off line buffering
ENABLE_PROCESSED_OUTPUT equ 1 ;Flag to turn off line bufferin
ENABLE_LINE_WRAP equ 3 ;Flag to trun line wrap on
DISABLE_PROCESSED_INPUT equ 7 ;Flag to turn on line buffering
CREATE_NEW EQU 1 ;Parameter for creating a new file
CREATE_ALWAYS EQU 2 ;Always create (overwrite existing)
OPEN_EXISTING EQU 3 ;Parameter for opening an existing file
GENERIC_READ EQU 80000000h ;Parameter for reading a file
GENERIC_WRITE EQU 40000000h ;Parameter for writing a file
FILE_SHARE_READ equ 1
FILE_SHARE_WRITE equ 2
FILE_SHARE_DELETE equ 4
FILE_ATTRIBUTE_NORMAL equ 80h
INVALID_HANDLE_VALUE equ 0FFFFFFFFh
HANDLE equ dword
;----------------------------------------------------------
; prototype Declarations for libarary imports
;----------------------------------------------------------
ExitProcess proto,
dwExitCode:dword ;The exit code for the process
GetStdHandle proto,
nStdHandle: dword ;The standard device. -10=INPUT, -11=OUTPUT, -13=ERROR
SetConsoleMode proto,
hConsoleHandle:dword, ;A handle to the console input buffer or a console screen buffer
dwMode:dword ;The input or output mode to be set.
ReadFile proto,
hFile:dword, ;A handle to the device
lpBuffer:near32, ;A pointer to the buffer that receives the data read
nNumberOfCharsToRead:dword, ;The maximum number of bytes to be read.
lpNumberOfbytesRead:near32, ;A pointer to the variable that receives the number of bytes read
lpOverlapped:near32 ;A pointer to an OVERLAPPED structure is required if the hFile parameter
;was opened with FILE_FLAG_OVERLAPPED, otherwise it can be NULL.
WriteFile proto,
hFile:dword, lpBuffer:near32, ;A handle to the device
nNumberOfCharsToWrite:dword, ;The maximum number of bytes to be written.
lpNumberOfbytesWritten:near32, ;A pointer to the variable that receives the number of bytes written
lpOverlapped:near32 ;A pointer to an OVERLAPPED structure is required if the hFile parameter
;was opened with FILE_FLAG_OVERLAPPED, otherwise it can be NULL.
CloseHandle proto, ;Prototype for closing a file
fHandle:dword
GetLastError proto ;Prototype for getting specific error
CreateFileA proto, ;Prototype for CreateFile, used for getting handle to new or existin file
lpFileName:near32,
dwDesiredAccess:dword,
dwShareMode:dword,
lpSecurityAttributes:near32,
dwCreationDisposition:dword,
dwFlagsAndAttributes:dword,
hTemplateFile:dword
GetConsoleScreenBufferInfo proto,
hConsoleOutput:dword, ; A handle to the console screen buffer.
lpConsoleScreenBufferInfo:near32 ; A pointer to a CONSOLE_SCREEN_BUFFER_INFO structure
FillConsoleOutputCharacterA proto,
hConsoleOutput:dword, ; A handle to the console screen buffer.
cCharacter:byte, ; The character to be written to the console screen buffer.
nLength:dword, ; The number of character cells to which the character should be written.
dwWriteCoord:dword, ; A COORD structure that specifies the character coordinates of the first cell
lpNumberOfCharsWritten:near32 ; A pointer to a variable that receives the number of characters actually written
SetConsoleCursorPosition proto,
hConsoleOutput:dword, ; A handle to the console screen buffer.
dwCursorPosition:dword ; new cursor position,
;----------------------------------------------------------
; Data Segment -- Global Variables
;----------------------------------------------------------
.data
strAddr dd ?
strLength dd ?
hStdOut dd ?
hStdIn dd ?
hFileOut dd ?
hFileIn dd ?
read dd ?
written dd ?
coord dd 0
scrnBufInfo db 22 DUP(0)
inFilename db 256 dup(0)
outFilename db 256 dup(0)
fdata db BUF_SIZE dup(0)
numBytes dd ?
newlineStr db NEWLINE, 0 ;string for printing newline
filePrompt db "Enter filename: ",0
mnuItem1 db "1) Enter new line",NEWLINE,0
mnuItem2 db "2) Read from file",NEWLINE,0
mnuItem3 db "3) Save to File",NEWLINE,0
mnuItem4 db "4) Exit",NEWLINE,0
mnuPrompt db ": ",0
invalidOption db "Invalid Option! ",0
continuePrompt db "Press enter key to continue...",NEWLINE,0
fileError db "Error reading file!",NEWLINE,0
goodbyeMsg db "Goodbye.", NEWLINE, 0
enterDataMsg db "Enter data:",NEWLINE,0
enterOutfilename db "Enter a name for file:",NEWLINE,0
newData db BUF_SIZE dup(0)
sectNum1 word 0 ;for holding the section number entered by user
sectNum2 word 0 ;for holding the section number entered by user
outData db BUF_SIZE dup(0) ; the finished string to be displayed
tempData db BUF_SIZE dup(0) ; extra string for temperary holding
sepStr db "-------------------------------------", NEWLINE, 0
zeroStr db "0", 0
spaceStr db " ",0
hexStr db "0x", 0
input1 db 80d dup(0) ;buffer for user input
output1 db 80d dup(0) ;buffer for output strings
console_buffer_info db 22 dup(0)
topLeft dword 0
;----------------------------------------------------------
; Code Segment
;----------------------------------------------------------
.code
main proc
MainLoop:
; zero out registers
xor eax, eax ; zero out eax
xor ebx, ebx ; zero out ebx
xor ecx, ecx ; zero out ecx
xor edx, edx ; zero out edx
lea esi, newlineStr
call PrintString
lea esi, sepStr
call PrintString
call DisplayMenu
; get Menu Option
lea edi, input1
call GetString
cmp input1, '1'
je option1
cmp input1, '2'
je option2
cmp input1, '3'
je option3
cmp input1, '4'
je Exit
; invalid option entered
lea esi, invalidOption
call PrintString
call PauseForEnter
jmp MainLoop
option1:
;display enter data prompt
lea esi, enterDataMsg
call PrintString
;get data from stdin
lea edi, outData
call GetString
;copy outData back to fdata
call Update_fdata
;clear display and display data at top
call ClearScreen
lea esi, fdata
call PrintString
jmp MainLoop
option2:
; prompt for filename to read
lea esi, filePrompt
call PrintString
; read filename from console
lea edi, inFilename
call GetString
; read file data
call ReadFileContents
;clear display and display data at top
call ClearScreen
lea esi, fdata
call PrintString
jmp MainLoop
option3:
;prompt to enter outFile name
lea esi, enterOutfilename
call PrintString
;get the outFile name
lea edi, outFilename
call GetString
;copy outData back to fdata
call Update_fdata
;call WriteFileContents to save file
call WriteFileContents
;clear display and display data at top
call ClearScreen
lea esi, fdata
call PrintString
jmp MainLoop
Exit:
lea esi, goodbyeMsg
call PrintString
invoke ExitProcess, 0 ;exit process with no error
main endp
;------------------------------------------------------------------------------
; Procedure to copy outData into fdata
;------------------------------------------------------------------------------
Update_fdata proc ; Define procedure
pushad ; save all registers
pushfd ; save flags
lea esi, outData
lea edi, fdata
;Copy Section
Update_fdata_loop1:
cmp byte ptr [esi], 0
je done_Update_fdata
mov al, [esi]
mov [edi], al
inc esi
inc edi
jmp Update_fdata_loop1
;;;;;;;;;;;;;;;;;;;
done_Update_fdata:
mov al, 0
mov [edi], al ;end EDI with null terminator
popfd ; restore flags
popad ; restore registers
ret ; return to caller
Update_fdata endp
;------------------------------------------------------------------------------
; Procedure to clear the screen
;------------------------------------------------------------------------------
ClearScreen proc
pushad ; save all registers
pushfd ; save flags
; HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
invoke GetStdHandle,STD_OUTPUT ; get handle for console output
mov hStdOut, eax ; copy file handle for screen
; GetConsoleScreenBufferInfo(console, &screen);
invoke GetConsoleScreenBufferInfo,hStdOut,near32 ptr console_buffer_info
; FillConsoleOutputCharacterA(console, ' ', screen.dwSize.X * screen.dwSize.Y, topLeft, &written);
xor eax, eax
xor ebx, ebx
mov ax, word ptr console_buffer_info
mov bx, word ptr console_buffer_info+2
mul bx
invoke FillConsoleOutputCharacterA,hStdOut,' ',eax,topLeft,near32 ptr written
; SetConsoleCursorPosition(console, topLeft);
invoke SetConsoleCursorPosition,hStdOut,topLeft
popfd ; restore flags
popad ; restore registers
ret ; return to caller
ClearScreen endp
;------------------------------------------------------------------------------
; Procedure to display the menu
;------------------------------------------------------------------------------
DisplayMenu proc
lea esi, sepStr
call PrintString
; Option 1: enter new line
lea esi, mnuItem1
call PrintString
; Option 2: save to file
lea esi, mnuItem2
call PrintString
; Option 3: Exit
lea esi, mnuItem3
call PrintString
; Option 4: Exit
lea esi, mnuItem4
call PrintString
lea esi, mnuPrompt
call PrintString
ret ; return to caller
DisplayMenu endp
;------------------------------------------------------------------------------
; Procedure to read file
; [IN] inFilename
; [OUT] file contents in fdata
;------------------------------------------------------------------------------
ReadFileContents proc ; Define procedure
pushad ; save all registers
pushfd ; save flags
; zero buffer out
lea edi, fdata ; point edi at buffer to be zero'd out
xor eax, eax ; put zero into accumulator to write to buffer
mov cx, BUF_SIZE ; put buf size into counter
rep stosb ; now fill buffer
; open file for reading
invoke CreateFileA, near32 ptr inFilename, GENERIC_READ, FILE_SHARE_READ,
0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
mov hFileIn, eax ; save file handle
; see if we got an error opening file
cmp eax, INVALID_HANDLE_VALUE
je error_ReadFileContents
mov ecx, BUF_SIZE ; string length
lea edi, fdata ; load address of fdata into edi
invoke ReadFile, ; invoke standard ReadFile with
eax, ; file handle for keyboard
edi, ; address of location to write file contents
ecx, ; length of string
near32 ptr read, ; variable for # bytes read
0 ; overlapped mode
invoke CloseHandle, hFileIn; close file handle
; display # bytes read
mov eax, read ; move num bytes read into eax, so we can save to variable
mov numBytes, eax ; now save value from eax into permanent variable
;lea esi, read ; point esi at num bytes read for converting to string
;mov ebx, 10 ; set our number base to base 10
;lea edi, output1 ; point edit at location to store string
;call Num2Str ; now convert number to a string
;mov esi, edi ; now point esi at that string to display
;call PrintString ; display the string
;lea esi, bytesReadStr ; point esi at " bytes read." string
;call PrintString ; display it
jmp done_ReadFileContents ; jump over error handling section
error_ReadFileContents:
lea esi, fileError ; point esi at file read error message
call PrintString ; now display the message
done_ReadFileContents:
popfd ; restore flags
popad ; restore registers
ret ; return to caller
ReadFileContents endp
;------------------------------------------------------------------------------
; Procedure to write data to file
; [IN] file contents in fdata
; [IN] outFilename
;------------------------------------------------------------------------------
WriteFileContents proc ; Define procedure
pushad ; save all registers
pushfd ; save flags
invoke CreateFileA, near32 ptr outFilename, GENERIC_WRITE, FILE_SHARE_WRITE,
0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
mov hFileOut, eax
lea esi, fdata
invoke WriteFile, ; invoke standard WriteFile with
hFileOut, ; file handle for screen
esi, ; address of string
numBytes, ; length of string
near32 ptr written, ; variable for # bytes written
0 ; overlapped mode
invoke CloseHandle, hFileOut; close file handle
; display number of bytes written
;lea esi, numBytes ; point esi at num bytes read for converting to string
;mov ebx, 10 ; set our number base to base 10
;lea edi, output1 ; point edit at location to store string
;call Num2Str ; now convert number to a string
;mov esi, edi ; now point esi at that string to display
;call PrintString ; display the string
;lea esi, bytesWriteStr ; point esi at " bytes written." string
;call PrintString ; display it
popfd ; restore flags
popad ; restore registers
ret ; return to caller
WriteFileContents endp
;------------------------------------------------------------------------------
; Procedure to prompt user to press enter to continue
;------------------------------------------------------------------------------
PauseForEnter proc
lea esi, continuePrompt ; point esi at string to prompt user to continue
call PrintString ; display prompt
lea esi, input1 ; point esi at location for user input
call GetString ; get user input from console
ret ; return to caller
PauseForEnter endp
;------------------------------------------------------------------------------
; Procedure to print a string to stdout
;
; Given : The Address of Null (0) terminated String to print in ESI register
; process : Print the String using the kernel32.lib WriteFile to
; : Standard_Output function call. No registers are changed and the
; : flags are not affected.
; Return : Nothing
;------------------------------------------------------------------------------
PrintString proc ; Define procedure
pushad ; save registers
pushfd ; save flags
mov strAddr, esi ; copy string address
; find string length
mov strLength, 0 ; initialize string length
WhileChar: cmp byte ptr [esi], 0 ; character = null?
jz EndWhileChar ; exit if so
inc strLength ; increment character count
inc esi ; point at next character
jmp WhileChar ; while more characters exist
EndWhileChar:
invoke GetStdHandle,STD_OUTPUT ; get handle for console output
mov hStdOut, eax ; copy file handle for screen
invoke WriteFile, ; invoke standard WriteFile with
hStdOut, ; file handle for screen
strAddr, ; address of string
strLength, ; length of string
near32 ptr written, ; variable for # bytes written
0 ; overlapped mode
popfd ; restore flags
popad ; restore registers
ret ; return to caller
PrintString endp
;------------------------------------------------------------------------------
; Procedure to get a string from stdin
;
; Given : The Address of the String to fill in EDI register
; process : Input the String using the kernel32.lib ReadFile from the
; : Standard_Input function call. No registers are changed and the
; : flags are not affected.
; Return : The input string in the data segment
;------------------------------------------------------------------------------
GetString proc ; Define procedure
pushad ; save all registers
pushfd ; save flags
invoke GetStdHandle,STD_INPUT ; get handle for console
mov hStdIn, eax ; save the handle
invoke SetConsoleMode, ; invoke standard console with
hStdIn, ; file handle for keyboard
DISABLE_PROCESSED_INPUT ; turn line buffering on
mov ecx, 255d;MAXSTR ; string length
mov strLength, ecx ; maximum string to accept
mov strAddr, edi ; save pointer to input string
invoke ReadFile, ; invoke standard ReadFile with
hStdIn, ; file handle for keyboard
strAddr, ; address of string
strLength, ; length of string
near32 ptr read, ; variable for # bytes read
0 ; overlapped mode
mov ecx, read
mov byte ptr [edi+ecx-2],0 ; replace CR/LF by trailing null
popfd ; restore flags
popad ; restore registers
ret ; return to caller
GetString endp
;------------------------------------------------------------------------------
; Procedure to print a character to the console
;
; Given : The Address of the Character to print in ESI register
; Process : Print the Character using the kernel32.lib WriteFile to
; : Standard_Output function call. No registers are changed and the
; : flags are not affected.
; Return : Nothing
;------------------------------------------------------------------------------
PutChar PROC NEAR32 ; Define procedure
pushad ; save registers
pushfd ; save flags
INVOKE GetStdHandle,STD_OUTPUT ; get handle for console output
mov hStdOut, eax ; copy file handle for screen
INVOKE SetConsoleMode, ; invoke standard console with
hStdOut, ; file handle for screen
ENABLE_PROCESSED_OUTPUT ; turn line buffering off
INVOKE WriteFile, ; invoke standard WriteFile with
hStdOut, ; file handle for screen
esi, ; address of character
1, ; length of one byte
NEAR32 PTR written, ; variable for # bytes written
0 ; overlapped mode
popfd ; restore flags
popad ; restore registers
ret ; return to caller
PutChar ENDP
;------------------------------------------------------------------------------
; Procedure to get a character from the console
;
; Given : The Address of the Character to get in ESI register
; Process : Input the Character using the kernel32.lib ReadFile from the
; : Standard_Input function call. No registers are changed and the
; : flags are not affected.
; Return : The input character in the data segment
;------------------------------------------------------------------------------
GetChar PROC NEAR32 ; Define procedure
pushad ; save all registers
pushfd ; save flags
INVOKE GetStdHandle,STD_INPUT ; get handle for keyboard
mov hStdIn, eax ; save the handle
INVOKE SetConsoleMode, ; invoke standard console with
hStdIn, ; file handle for keyboard
ENABLE_PROCESSED_INPUT ; turn line buffering off
INVOKE ReadFile, ; invoke standard ReadFile with
hStdIn, ; file handle for keyboard
esi, ; address of character
1, ; length of one byte
NEAR32 PTR read, ; variable for # bytes read
0 ; overlapped mode
call PutChar ; echo the character on screen
popfd ; restore flags
popad ; restore registers
ret ; return to caller
GetChar ENDP
;------------------------------------------------------------------------------
; Procedure to convert a null terminated string into a word-sized unsigned number
;
; Inputs: ESI points to null terminated string to convert
; EDI points to location to store answer
; BX contains number base of given number
; Outputs: unsigned word stored in location that EDI points to
;------------------------------------------------------------------------------
Str2Num proc
pushad ; save registers
pushfd ; save flags
; initialize total to 0
xor eax, eax ; using accumulator for total
xor ecx, ecx ; zero out all of ecx
; loop through each character in string
charLoop_str2num:
; if null terminator, exit loop
cmp byte ptr [esi], 0
je done_str2num
; if space terminator, exit loop
cmp byte ptr [esi], ' '
je done_str2num
; if not null, process
; weighted positional notation
; multiply total times base
; dx:ax = ax * bx
mul bx
; convert digit to number and add to total
mov cl, [esi] ; move ascii character into register
cmp cl, '9' ; see if character is numeric digit
jg isAlpha_str2num
; else, fall through... treat as numeric digit
isNumeric_str2num:
sub cl, '0' ; converting character to number, ie. '1' => 1
jmp addNumberToTotal
isAlpha_str2num:
; make characters uppercase
and cl, 0dfh ; AND with 1101 1111 to turn off bit
sub cl, 'A' ; 'A'=0, 'B'=1, 'C'=2, ...
add cl, 10 ; 'A'=10, 'B'=11, 'C'=12, ...
addNumberToTotal:
add ax, cx ; add number to total
; increment pointer to next character
inc esi
; continue with loop
jmp charLoop_str2num
done_str2num:
; number is in AX... let's store answer
mov [edi], ax
popfd ; restore flags
popad ; restore registers
ret ; return to caller
Str2Num endp
;------------------------------------------------------------------------------
; Procedure to convert a word-sized unsigned number into a null terminated string
;
; Inputs: ESI points to unigned word-size integer to convert
; EDI point to location to store null terminated string
; BX contains number base of desired output string
; Outputs: unsigned word stored in location that EDI points to
;------------------------------------------------------------------------------
Num2Str proc
pushad ; save registers
pushfd ; save flags
mov ax, [esi] ; move number into accumulator for divide remainder technique
pushw 0 ; push null terminator onto stack to mark position
; loop until zero
divLoop:
xor dx, dx ; prep dx register for divide
div bx ; divide ax/bx
; remainder can be 0-9... or 10+
cmp dx, 9
jg alpha_num2str
digit_num2str:
add dx, '0' ; convert number to ascii character, ie. 7=>'7'
jmp push_num2str
alpha_num2str:
sub dl, 10 ; 10=>0, 11=>1, 12=>2, ...
add dl, 'A' ; 0=>A, 1=>B, 2=>C, ...
push_num2str:
push dx ; save character onto stack
cmp ax, 0 ; if accum is zero... done with div remainder loop
jne divLoop ; if AX != 0... continue with divide remainder
popCharacters:
pop dx ; pop character off stack
mov [edi], dl ; store character onto string
inc edi ; move pointer to next byte
cmp dl, 0 ; check if null terminator
jne popCharacters ; if not null terminator, continue processing characters
done_num2str:
popfd ; restore flags
popad ; restore registers
ret ; return to caller
Num2Str endp
end ; end directive to compiler
Using the 32-bit registers instead of the 8-bit registers in the Update_fdata procedure solved my problem with the Chinese characters showing in my created txt files. 在Update_fdata过程中使用32位寄存器而不是8位寄存器解决了我的问题,因为在创建的txt文件中显示了中文字符。
;----------------------------------------------------------------------
; Procedure to copy outData into fdata
;----------------------------------------------------------------------
Update_fdata proc ; Define procedure
pushad ; save all registers
pushfd ; save flags
xor ecx, ecx
lea esi, outData
lea edi, fdata
;Copy Section
Update_fdata_loop1:
cmp byte ptr [esi], 0
je done_Update_fdata
mov eax, [esi]
mov [edi], eax
inc esi
inc edi
inc ecx
jmp Update_fdata_loop1
;;;;;;;;;;;;;;;;;;;
done_Update_fdata:
mov eax, 0
mov [edi+1], eax ;end EDI with null terminator
inc ecx
mov numBytes, ecx ; now save value from eax into permanent variable
popfd ; restore flags
popad ; restore registers
ret ; return to caller
Update_fdata endp
As @Michael Petch pointed out numBytes wasn't getting updated whenever I was copying over bytes in the Update_fdata procedure. 正如@Michael Petch指出的,每当我在Update_fdata过程中复制字节时,numBytes都不会更新。 This caused WriteFileContents unable to produce output to the out file.
这导致WriteFileContents无法产生输出到out文件。 Using ECX as a counter and moving the value into numbytes at the end of the Update_fdata procedure corrected this issue.
使用ECX作为计数器,并在Update_fdata过程结束时将其值移动到numbytes中,可以解决此问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.