#windows #assembly #x86 #masm
#Windows #сборка #x86 #masm
Вопрос:
У меня есть файл, который содержит по одному слову в каждой строке (количество слов и их длина неизвестны), и мне нужно переписать эти слова в другой файл, начиная с последнего слова и заканчивая первым. Когда я печатаю последнее слово в файле, я пытаюсь настроить курсор (seek_cur) на поиск следующего слова, но он устанавливает его в неизвестное место. Попробовал распечатать текущий курсор, чтобы посмотреть, что произойдет, и он выдает символы типа «@A».
Второй jmp get_out останавливает программу после записи последнего слова, если оно удалено, оно переходит к тегу поиска jmp, а затем печатает одно и то же последнее слово бесконечно.
.386
.model flat, stdcall
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;includem biblioteci, si declaram ce functii vrem sa importam
includelib msvcrt.lib
extern exit: proc
extern fopen: proc
extern fclose: proc
extern fscanf: proc
extern fprintf: proc
extern fseek: proc
extern fgets:proc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;declaram simbolul start ca public - de acolo incepe executia
public start
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;sectiunile programului, date, respectiv cod
.data
file_read db "r",0
file_write db "w",0
file_name_read db "read.txt",0
file_name_write db "write.txt",0
seek_end dd 2
seek_cur dd 1
seek_set dd 0
caracter_format db "%c",0
string_format db "%s",0
decimal_format db "%d",0
string db 0
caracter db 0
back dd 0
first_word db 0
.code
start:
push offset file_read
push offset file_name_read
call fopen
add esp,8
mov esi,eax
push offset file_write
push offset file_name_write
call fopen
add esp,8
mov edi,eax
;in first_word se pune cuvantul de pe prima linie ca sa fie posibila comparatia mai incolo si sa se iese din bucla cand se ajunge la primul cuvant
repeat_search_first_word:
push offset caracter
push offset caracter_format
push esi
call fscanf
add esp,12
inc back
cmp caracter,0Ah
je out_of_search_first_word
jmp repeat_search_first_word
out_of_search_first_word:
inc back
neg back
push seek_cur
push back
push esi
call fseek
add esp,12
neg back
push esi
push back
push offset first_word
call fgets
add esp,12
mov back,0
;incepe cautarea cuvintelor de la capat
push seek_end
push -1
push esi
call fseek
add esp,12
search:
push offset caracter
push offset caracter_format
push esi
call fscanf
add esp,12
inc back
cmp caracter,0Ah
jne is_caracter
push esi
push back
push offset string
call fgets
add esp,12
push offset string
push offset string_format
push edi
call fprintf
add esp,12
;testam daca cuvantul coincide cu primul (first_word)
mov ebx,0
mov bl,string
cmp bl,first_word
je get_out
add back,2
neg back
;!!!!!!!!!!!!!!
;problema pentru rularea infinita ii aici fiindca seek_cur muta cursorul intr-o zona necunoscuta din fisier
push seek_cur
push back
push esi
call fseek
add esp,12
mov back,0
jmp get_out ;linia 152 lasata fara comentariu permite afisare ultimului cuvant fara sa intre in rularea infinita a buclei
jmp search
is_caracter:
push seek_cur
push -2
push esi
call fseek
add esp,12
jmp search
get_out:
push edi
call fclose
add esp,4
push esi
call fclose
add esp,4
push 0
call exit
end start
read.txt содержит:
abc
defg
hijklm
write.txt должно быть:
hijklm
defg
abc
Комментарии:
1. не проблема, но
seed_end = 2
сделать его постоянной времени сборки имело бы гораздо больше смысла, чем загрузка dword2
из статического хранилища! Кроме того,fgetc
это намного проще и эффективнее, чемfscanf(fp, "%c")
, и вернет символ в регистр, где вы могли бы прочитать его более эффективно, чем из памяти. Или вы могли бы использоватьfscanf(fp, "%s")
, потому%s
что пропускает пробелы.
Ответ №1:
string db 0
резервирует место для 1 байта (инициализируется нулем).
Затем вы вызываете fgets(fp, string, back)
, который перезапишет более поздние данные в вашем разделе данных, если он считывает более 1 байта (включая завершающий 0).
Используйте больший буфер в BSS, например, пару МБ или что-то в этом роде.
Используйте отладчик для отслеживания вызовов функций / системных вызовов. В Linux вы можете использовать ltrace
для отслеживания функций libc stdio или strace
для отслеживания системных вызовов, которые они используют. В Windows IDK. Вы всегда можете просмотреть и просмотреть аргументы, которые вы поместили в стек перед каждым вызовом, чтобы убедиться, что они в порядке, но часто бывает проще увидеть список в стиле файла журнала, если вы ищете один с неправильными аргументами.
Комментарии:
1. Изменил «строку db 0» на «строку dd 0», и это сработало мгновенно. Большое спасибо!
2. @Kenshoru: э-э, это все равно сломается, если вы прочитаете строку длиной более 3 байт ( 1 ограничитель). Используйте что-то вроде
.bss
/string db 1024*1024 dup(?)
3. Заметил это в конце концов, он отлично работал с 3 символьными словами.. Я посмотрю на это. Спасибо!