Консультация № 183613
14.06.2011, 13:07
57.02 руб.
0 7 1
Уважаемые эксперты! Пожалуйста, ответьте на вопрос: Повторяю свой вопрос № 183147 -написать программу, считающую количество запятых в файле. Имя файла ввести с клавиатуры или передать как параметр командной строки. Чтение из файла организовать блоком. Размер блока- по усмотрению.Необходимы подробные комментарии на каждой строке.АСС- TASM.В приложении ответ данный Зенченко К.Н. Если можно упростите программу, так как мы только начинаем изучать АСС ? Заранее спасибо, с уважением Canijke.




Приложение:
model tiny
.code
org 100h
szBlock equ 256
;начало сбрасываем счетчик
begin: xor ax,ax
mov dwCount,ax
;адрес командной строки
mov si,80h
;адрес буфера
lea di,dbFileName
lodsb;длина аргументов ком строки
or al,al
jz cmd01
;копируем командную строку
xor ch,ch
mov cl,al
lodsb;пропускаем пробел
push di
rep movsb;копируем вместе с 0Dh
pop di
jmp cmd02
;вводим имя
cmd01: mov ah,09;сообщение "введите имя"
lea dx,dbEnter
int 21h
mov ax,0FFh;длина 256 символов
mov dwHndl,ax
lea dx,dwHndl;вводим имя файла
mov ah,0ah
int 21h
;ком.строка и ввод строки всегда заканчивается 0Dh
cmd02: inc di
cmp byte ptr[di],0Dh
jnz cmd02
;нам нужно для открытия файла заменить его на 0
xor ax,ax
stosb
;открываем файл
mov ah,3Dh
lea dx,dbFileName
int 21h
jnc @@01
;ошибка выводим сообщение
lea dx,dbErrorOpen
jmp MSG01
;
@@01: mov dwHndl,ax;запоминаем дискриптор
;читаем блок данных
@@02: mov ah,3Fh
mov bx,dwHndl
mov cx,szBlock
lea dx,dbBlock
int 21h
jnc @@03
;ошибка закрываем файл
mov ah,3eh
mov bx,dwHndl
int 21h
;выводим сообщение
lea dx,dbErrorLoad
MSG01: lea bx,AnyKey
push bx
jmp MSG
;в счетчик количество прочитанных символов
@@03: mov cx,ax
push ax;запоминаем их
lea si,dbBlock;настраиваемся на блок памяти
@@04: lodsb;считаем символ
cmp al,',';запятая
jnz @@05;нет переходим
inc word ptr dwCount;да увеличиваем счетчик
@@05: loop @@04;пока СХ больше нуля
pop ax;востанавливаем кол-во символов
cmp ax,szBlock;проверяем кол-во символов
jz @@02;совпало повторяем
;не совпало конец файла, закрываем его
mov ah,3Eh
mov bx,dwHndl
int 21h
;выводим сообщение "результат"
lea dx,dbResult
call MSG
;загружаем счетчик
mov ax,dwCount
xor cx,cx
mov bx,10
;в цикле остаток от деления на 10 записываем в стек
@@06: xor dx,dx
div bx
push dx
inc cx
or ax,ax
jnz @@06
;в цимклу из стека извлекаем цифры в нужной последовательности
@@07: pop ax
or al,'0'
int 29h
loop @@07
;ожидаем любую клавишу
AnyKey: xor ax,ax
int 16h
ret
;выводим сообщение адрес DX
MSG: mov ah,9
int 21h
ret
dbEnter db 10,13,'Enter file name:$'
dbResult db 10,13,'Result:$'
dbErrorOpen db 10,13,'Error open file$'
dbErrorLoad db 10,13,'Error load data$'
dwCount dw ?
dwHndl dw ?
dbFileName label byte
dbBlock db szBlock dup(?)
end begin

Обсуждение

давно
Старший Модератор
31795
6196
14.06.2011, 14:07
общий
Что именно нужно упростить?
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
14.06.2011, 16:22
общий
Если можно, написать более простой код
давно
Посетитель
7438
7205
14.06.2011, 16:49
общий
Могу предложить свой вариант... Хотите?
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
14.06.2011, 16:57
общий
Да, пожалуйста, Игорь Витальевич!И комментарии тоже. Огромная благодарность за комментарии к прошлому вопросу...
давно
Посетитель
7438
7205
14.06.2011, 17:56
общий
это ответ
Здравствуйте, Canijke!

Вот Вам моя версия программы.
Разные подзадачи реализованы в виде подпрограмм.
Программа анализирует сначала параметр командной строки, причем игнорирует лидирующие пробелы и
пробел, также как и код 0dh, является концом имени файла.
Если параметра нет, то имя запрашивается с консоли.
При операциях с файлом проводится проверка на ошибки.

Придется-таки потрудиться, чтобы понять код... Задача достаточно объемная.
Если что непонятно, пишите в мини-форум
Удачи!

[code h=207]
.model small
.stack 100h
.code
start:
mov ax, @data
mov es, ax ;настроим пока только регистр es
;ds равен сегменту PSP, там параметр командной строки
call GetParm ;смотрим параметр из командной строки, если есть,
;то dx - смещение имени файла в сегменте данных
mov ax, @data
mov ds, ax ;настроим и ds

jnc openFile ;если задано, то на открытие файла
;не задано - запрашиваем
lea dx, sGetIn ;строка приглашения 'Enter file name:'
call GetName ;ds:dx = адрес имени входного файла

;откроем входной файл
openFile:
mov ax, 3d00h ;открываем файл на чтение
int 21h
jc OpenError ;ошибка?
mov bx, ax ;сохраним описатель файла в bx для дальнейшей работы
xor si, si ;счетчик запятых
lea dx, buffer ;адрес буфера
xor di, di ;для контроля файла 0 длины
;прочитаем
readLoop:
mov cx, 1000h ;будем читать 4к за 1 раз
mov ah, 3fh ;функция чтения
int 21h
jc ReadError ;ошибка?
mov cx, ax ;сохраним длину файла в cx
jcxz printRes ;чтение после конца даст 0 - признак, что все прочитано

call CalcCommas ;считаем запятые кусочка
jmp readLoop ;и на повтор

printRes: ;вывод результата подсчета
lea dx, sCommasNum ;выведем сообщение
mov ah, 9
int 21h

mov ax, si ;число запятых
call PrintNum

closeFile:
mov ah, 3eh ;закроем входной файл
int 21h

PressAny:
lea dx, sAny ;выведем 'Press any key'
mov ah, 9
int 21h

mov ah, 0 ; ждем нажатие на клавишу
int 16h

mov ax,4c00h ; конец работы
int 21h

;обработка ошибок
OpenError: ;ошибка открытия (файл или не найден, или занят)
lea dx, sOpenErr
mov ah, 9
int 21h
jmp PressAny ;на вывод 'Press any key'
ReadError: ;ошибка чтения
lea dx, sReadErr
mov ah, 9
int 21h
jmp closeFile ;на закрытие и вывод 'Press any key'

CalcCommas proc ;подсчет запятых, dx - адрес буфера, cx - длина, результат - si
mov di, dx ;адрес начала буфера
mov al, ',' ;будем искать код ','
calcLoop:
jcxz calcRet ;длина 0 - нечего делать
repne scasb ;ищем, пока не равно
jne calcRet ;не равно, значит дошли до конца
inc si ;равно - считаем
jmp calcLoop ;по всему буферу
calcRet:
ret
CalcCommas endp

;Подпрограмма ввода имени файла
;на входе: ds:dx - адрес сообщения
;на выходе: ds:dx - адрес имени
GetName proc
mov ah, 9 ;выведем приглашение из dx:dx
int 21h

lea dx, buf ;введем строку
mov ah, 0ah
int 21h

xor bx, bx ;заменим последний код 0dh на 0
mov bl, len ;длина введенной строки
mov byte ptr string[bx], 0 ;пишем 0

lea dx, string ;возвращаем адрес имени
ret
GetName endp

;анализ параметра
;адрес параметра в PSP находится по адресу PSP:80h - длина, PSP:81h - сам параметр,
;заканчивающийся кодом 0dh. Мы длину трогать не будем, а будем анализировать по коду 0dh
;имя файла можно как предворять, так и завершать разделителями (пробелом, табуляцией)
;флагом CF = 0/1 вернем результат: задано/не задано имя файла
GetParm proc
mov si, 81h ;параметр в сегменте PSP
lea di, string ;адрес в сегменте данных, куда запишем имя файла
mov dx, di ;здесь вернем адрес имени, если задано, конечно
xor cx, cx ;счетчик символов имени файла
parmLoop:
lodsb ;очередной
cmp al, 0dh
je endParm ;конец строки
cmp al, ' '
je parmSeparator ;разделитель
cmp al, 9
je parmSeparator ;разделитель
stosb ;символ имени - копируем
inc cx ;считаем
jmp ParmLoop ;по всем

parmSeparator: ;разделитель
jcxz parmLoop ;разделители в начале строки пропускаем
endParm: ;конец строки
stc ;сначала пометим, что нет параметра
jcxz parmRet ;счетчик символов нет - точно нет параметра
mov al, 0 ;есть, завершаем строку нулем
stosb
clc ;и помечаем, что параметр задан
parmRet:
ret
GetParm endp

PrintNum proc ;вывод беззнакового числа из ax
mov bx, 10 ;будем делить на 10
xor cx, cx ;счетчик цифр
DivLoop:
xor dx, dx ;готовимся к делению dx:ax / bx
div bx ;ax - частное, dx - остаток=очередной младшей цифре
push dx ;сохраним цифру в стеке
inc cx ;посчитаем
test ax, ax ;продолжим, пока не 0
jnz DivLoop
mov ah, 2 ;функция вывода
PrintLoop: ;будем выводить в обратном порядке, начиная со старшей шифры
pop ax ;восстановим очередной разряд
or al, '0' ;превратим в символ
int 29h ;выведем
loop PrintLoop
ret
PrintNum endp

.data

;строки сообщений
sGetIn db 0dh,0ah,'Enter file name: $'
sCommasNum db 0dh,0ah,'Commas count = $'
sAny db 0dh,0ah,'Press any key$'

sOpenErr db 0dh,0ah,'File open error!$'
sReadErr db 0dh,0ah,'File read error!$'

buf label byte ; буфер для приема строки с клавиатуры (по ф-и 0ah)
max db 128 ; максимальная длина строки
len db 0 ; реальная длина введенной строки
string db 128 dup (?) ; сама строка

buffer db 1000h dup(?) ;буфер для чтения файла (размещаем за программой)

end start
[/code]
5
Спасибо за помощь. Надеюсь разобраться и сдать это задание.
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Старший Модератор
31795
6196
14.06.2011, 18:12
общий
14.06.2011, 20:21
Цитата: 377629
Если можно, написать более простой код

Куда ещё проще, из кода убрано всё лишнее.

Программа логически делится на три участка(выделено пустыми строчками).
1)анализ командной строки, получение имени файла и его открытие;
2)цикл задания, т.е. поиск и подсчет запятых в файле;
3)закрытие файла, и вывод результата.

Отдельно по каждому пункту:
1)Системный загрузчик в запущеную программу передает аргументы командной строки и записывает их в префиксе программы по адресу DS:080h. Первый байт содержит длину переданных аргументов. Если длина больше нуля, то второй байт будет пробелом, после этого сам параметр(ы) и все это заканчивается кодом 0Dh. Если параметров нет, то там будет ноль, а следующий байт будет содержать код 0Dh. Строки lodsb | or al,al | jz noCommandLine занимаются именно этой проверкой. Если есть параметр в командной строке, он копируется в буфер программы dbFileName label byte | dbBlock db szBlock dup(?) вместе с замыкающим кодом 0Dh. В случае, когда параметров нет - будет переход на метку noCommandLine и программа попросит пользователя ввести имя файла(с помощью функций 09h и 0Аh - прерывания ДОС 21h), Введеная строка будет размещена в буфере программы. Тем или иным способом программа получает имя проверяемого файла, но пока ещё не готова открывать его, т.к. для функции 3Dh прерывания ДОС 21h, необходимо, что бы строка заканчивалась кодом 0, а у нас там пока 0Dh. Исправлением этого занимается цикл setMarker: inc di | cmp byte ptr[di],0Dh | jnz setMarker, следующие строчки xor ax,ax | stosb записывают вместо найденого кода 0. Только после этого открывается сам файл. Если ошибки нет то переходим к следующему этапу, иначе выводится сообщение об ошибке и останов.

2)С помощью фунции 3Fh прерывания ДОС 21h из файла считывается блок данных размером szBlock (у нас это 256 байт). Если не было ошибки, то начинается цикл проверки, иначе файл закрывается, выводится сообщение об ошибке и останов. Цикл проверки:
Код:
		lea	si,dbBlock;настраиваемся на блок памяти
isLoop: lodsb ;считаем символ
cmp al,',';запятая
jnz ignore;нет переходим
inc word ptr dwCount;да увеличиваем счетчик
ignore: loop isLoop;пока СХ больше нуля

Для проверки используется строковая команда lodsb - содержимое памяти DS:SI помещается в аккумулятор, в данном случае это AL и значение SI увеличивается на размер операнда, сравнивается с искомым cmp al,',' и при совпадениии увеличивается счетчик. Так будет продолжатся пока не будет проверены все прочитанные данные. Следующие строчки проверяют файл на окончание:
Код:
isLoad:		mov	cx,ax
push ax;запоминаем их
. . .
pop ax;востанавливаем кол-во символов
cmp ax,szBlock;проверяем кол-во символов
jz LoadFile

После выполнения фунции чтения с диска в регистре АХ находится реально считаное количество байт, и если оно не совпадает со значением в регистре СХ, значит файл прочитан полностью и в нем больше нет информации для проверки.

3)Файл проверен полностью, его закрывают с помощью функции 3Еh прерывания ДОС 21h. Выводится результаты работы с помощью функции 09h прерывания ДОС 21h(выводится строка) и прерывания 29h само числовое значение.
Вывод числа делится на два этапа деление выводимого числа на десять и помещение остатка от деления в стек, после этого вывод полученых остатков от деления на экран в нужном порядке.


Код с добавлеными комментариями:
[code h=200]model tiny;модель памяти
.code;сегмент код-данных-стека
org 100h;стандартное смеение под заголовок
szBlock equ 256;размер выделеного блока
begin: xor ax,ax;содержимое Ах=0
mov dwCount,ax;сбрасываем счетчик
mov si,80h;DS:SI адрес командной строки переданного в программу системным загрузчиком
lea di,dbFileName;адрес имени файла
lodsb ;длина ком.строки
or al,al;равно нулю вводим имя
jz noCommandLine
;копируем командную строку
xor ch,ch;сбрасываем старший байт счетчика
mov cl,al;запоминаем длину счетчика
lodsb ;пропускаем пробел
push di;запоминаем индексный регистр
rep movsb;копируем вместе с 0Dh
pop di;востанавливаем индексный регистр
jmp setMarker
;в командной строке нет имени файла вводим его
noCommandLine: mov ah,09;сообщение "введите имя"
lea dx,dbEnter;адрес выводимого сообщения
int 21h
mov ax,0FFh;длина 256 символов
mov dwHndl,ax;запоминаем максимальную дляину строки
lea dx,dwHndl;вводим имя файла
mov ah,0ah;функция ДОС ввод строки
int 21h
;ком.строка и ввод строки всегда заканчивается 0Dh
setMarker: inc di;следющий символ
cmp byte ptr[di],0Dh;проверяем
jnz setMarker;не равен переходим
;нам нужно для открытия файла заменить его на 0
xor ax,ax; АХ=0
stosb ;записываем его в конец строки
mov ah,3Dh;функция открываем файл
lea dx,dbFileName;адрес с именем открываемого файла
int 21h
jnc isOpen;открыли без ошибки переходим
lea dx,dbErrorOpen;адрес сообщения об ошибке
jmp MSG01
isOpen: mov dwHndl,ax;запоминаем дискриптор
;
;
;читаем блок данных
LoadFile: mov ah,3Fh;функция чтения файла
mov bx,dwHndl;дискриптор открытого файла
mov cx,szBlock;размер читаемого блока
lea dx,dbBlock;адрес куда читаем
int 21h
jnc isLoad;прооверяем ошибку чтения
mov ah,3eh;функция закрыть файл
mov bx,dwHndl;дискриптор открытого файла
int 21h
lea dx,dbErrorLoad;сообщение об ошибке
MSG01: lea bx,AnyKey;адрес п/программы ожидание любой клавиши
push bx;запоминаем адрес в стеке
jmp MSG
;в счетчик количество прочитанных символов
isLoad: mov cx,ax
push ax;запоминаем их
lea si,dbBlock;настраиваемся на блок памяти
isLoop: lodsb ;считаем символ
cmp al,',';запятая
jnz ignore;нет переходим
inc word ptr dwCount;да увеличиваем счетчик
ignore: loop isLoop;пока СХ больше нуля
pop ax;востанавливаем кол-во символов
cmp ax,szBlock;проверяем кол-во символов
jz LoadFile;совпало повторяем
;
;
;не совпало конец файла, закрываем его
mov ah,3Eh;фукнкция закрыть файл
mov bx,dwHndl;дискриптор открытого файла
int 21h
;выводим сообщение "результат"
lea dx,dbResult;сообщение о результате
call MSG;подпрограмма вывода сообщения
;загружаем счетчик
mov ax,dwCount
xor cx,cx;счетчик цифр в стеке
mov bx,10;система счисления
;в цикле остаток от деления на 10 записываем в стек
isDiv: xor dx,dx
div bx;делим на 10-ть
push dx;остаток запоминаем в стеке
inc cx;увеличиваем счетчик
or ax,ax;пока не ноль повторяем деление
jnz isDiv
;в цикле из стека извлекаем цифры в нужной последовательности
isOut: pop ax;извлекаем из стека цифру
or al,'0';преобразовываем в символ
int 29h;выводим на экран
loop isOut;пока есть в стеке цифры
;ожидаем любую клавишу и выходим
AnyKey: xor ax,ax;функция ожидание клавиши без эхо
int 16h
ret
;выводим сообщение адрес DX
MSG: mov ah,9;функция вывода сообщения
int 21h
ret
dbEnter db 10,13,'Enter file name:$'
dbResult db 10,13,'Result:$'
dbErrorOpen db 10,13,'Error open file$'
dbErrorLoad db 10,13,'Error load data$'
dwCount dw ?
dwHndl dw ?
dbFileName label byte
dbBlock db szBlock dup(?)
end begin[/code]
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Посетитель
7438
7205
14.06.2011, 18:58
общий
Адресаты:
А почему не в ответ?
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Форма ответа