В этом симестре начали в универе изучать asm (Синтаксис Intel, компилятор TASM). Тут вот дали задание: "Полугодовая информация о подписных изданиях по каждому подписчику имеет структуру: ФИО, участок доставки, адрес, количество выписанных изданий, список изданий (название, газета или журнал, месяцы, на которые оформлена подписка). Создайте файл, в который запишите ФИО подписчиков, оформивших подписку на наибольшее число изданий, независимо от количества месяцев." А вот то, что я понаписал:
Код | ;-----------------------task3.asm---------------------------- ; Полугодовая информация о подписных изданиях по каждому ; подписчику имеет структуру: ФИО, участок доставки, адрес, ; количество выписанных изданий, список изданий (название, ; газета или журнал, месяцы, на которые оформлена подписка). ; Создайте файл, в который запишите ФИО подписчиков, ; оформивших подписку на наибольшее число изданий, независимо ; от количества месяцев. ;----------------------------------------------------------- ; Количество изданий на данный момент ограничено 9-ю. ;----------------------------------------------------------- .model tiny .code org 100h ; Начало .COM файла. ;----------------------------------------------------------- ; Начало программы. ;----------------------------------------------------------- _main: mov ah,3Dh ; Открытие входного файла mov al,11b ; для чтения и записи. lea dx,ifName int 21h jc open_error ; Перейти, если ошибка. mov ifId,ax ; Идентификатор файла в ifId. mov ah,5Bh ; Создание и открытие ; выходного файла. mov al,11b ; Для чтения и записи.????? xor cx,cx ; Без атрибутов. lea dx,ofName int 21h jc create_error ; Перейти, если ошибка. mov ofId,ax ; Идентификатор файла в ofId. ; Считывание и сравнение данных о подписчиках: do_read: call readData cmp dx,1 ; Сравнение DX с 1. jl exit ; Если DX = 0 - выход. jg if_eof ; Если DX = 2 - конец файла. call cmpMax ; Если DX = 1 - сравниваем. test dx,dx ; Сравниваем DX c 0. jz do_read ; Если DX = 0 - искомый ; подписчик не найден. call cpMax ; Если DX = 1 - искомый jmp do_read ; подписчик найден. if_eof: ; Обнаружен конец файла. call cmpMax ; Выполняем действия с test dx,dx ; последней записью в файле. jz do_write call cpMax
do_write: call writeMax ; Записываем данные в файл. test dx,dx jz exit open_error: ; Печать ошибки на экран. mov ah,09h lea dx,openError int 21h jmp exit create_error: ; Печать ошибки на экран. mov ah,09h lea dx,createError int 21h exit: ; Выход из программы. call doExit ;----------------------------------------------------------- ; Процедуры. ;----------------------------------------------------------- ; Назначение: ; Считывание данных из входного файла и запись их в anInfo. ; Выход: ; DX = 0, если произошла ошибка чтения. ; DX = 1, если обнаружен конец файла. ; DX = 2, если все нормально. readData proc near mov ax,ifId lea bx,anInfo.fio push ax push bx push 0020 ; Считываем ФИО. call getData pop ax ; Убираем адрес буфера pop ax ; и размер из стека. cmp dx,2 ; Если ошибка или конец файла, jl proc_exit ; то выходим из процедуры. lea ax,anInfo.dest push ax push 0015 ; Считываем участок доставки. call getData pop ax pop ax cmp dx,2 jl proc_exit lea ax,anInfo.address push ax push 0015 ; Считываем адрес. call getData pop ax pop ax cmp dx,2 jl proc_exit lea ax,anInfo.amount push ax push 0001 ; Считываем количество call getData ; выписанных изданий. pop ax pop ax cmp dx,2 jl proc_exit lea ax,anInfo.list push ax push 0256 ; Считываем список изданий. call getData pop ax pop ax
proc_exit: call doExit readData endp
; Назначение: Считывание данных в буфер до появления ; символа перехода на новую строку. ; Вход: ; Первый параметр в стеке - Адрес буфера. ; Второй параметр в стеке - Дескриптор файла. ; Выход: ; DX = 0, если произошла ошибка чтения. ; DX = 1, если обнаружен конец файла. ; DX = 2, если все нормально. getData proc near ; Параметры: parFId equ [bp+8] ; Дескриптора файла. parBuffer equ [bp+6] ; Адрес буфера. parBSize equ [bp+4] ; Размер буфера. ; Локальные переменные: myCounter equ [bp-2] ; Счетчик байтов. push bp ; Сохранить BP. mov bp,sp ; Установить BP для этой процедуры. sub sp,6 ; 6 байт для лок-х переменных. ; Тело процедуры: mov word ptr myCounter,0 mov ah,3Fh ; Считывание данных из файла. mov bx,parFId ; Идентификатор файла в BX. mov cx,1 mov dx,parBuffer ; Адрес буфера в DX. dec dx ; Уменьшаем на 1, чтобы начать с ; 0-го байта после INC DX. continue: inc dx inc word ptr myCounter ; >>>>>>> Вот тут проблема: >>>>>>>>> int 21h jc read_error ; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> cmp ax,1 ; Если ax < cx(1), то это конец файла. jl eof mov ax,parBSize cmp ax,myCounter ; Если размер буфера меньше кол-ва jl read_error ; считанных байтов - ошибка. mov ax,[dx] cmp ax,0Ah ; Если не был считан символ перехода, jne continue ; продолжаем работу. mov dx,2 ; Завершение работы процедуры. mov sp,bp ; LEAVE и ENTER не работают. =( pop bp ret read_error: ; Печать ошибки на экран. mov ah,09h lea dx,readError int 21h mov bx,0 mov sp,bp ; Выбросить из стека локальные pop bp ; переменные. Восстановить BP. call doExit
eof: ; Обнаружен конец файла. mov bx,1 mov sp,bp ; Выбросить из стека локальные pop bp ; переменные. Восстановить BP. ret ; Выход без удаления параметров ; из стека. getData endp
; Назначение: Сравнение данных с искомым подписчиком. ; Выход: ; DX = 0, новый искомый подписчик не найден. ; DX = 1, новый искомый подписчик найден. cmpMax proc near mov al,anInfo.amount mov ah,maxInfo.amount cmp al,ah jg new_max mov dx,0 ret
new_max: mov dx,1 ret cmpMax endp
;Назначение: Копирование данных в структуру искомого подписчика. cpMax proc near lea si,anInfo.fio lea di,maxInfo.fio rep movsb lea si,anInfo.dest lea di,maxInfo.dest rep movsb lea si,anInfo.address lea di,maxInfo.address rep movsb mov al,anInfo.amount mov maxInfo.amount,al lea si,anInfo.list lea di,maxInfo.list rep movsb ret cpMax endp
; Назначение: Запись искомого подписчика в выходной файл. ; Выход: ; DX = 0, если произошла ошибка записи. ; DX = 1, если все прошло успешно. writeMax proc near xor cx,cx mov ah,40h ; Запись в выходной файл. mov bx,ofId mov cl,20 ; Запись ФИО. lea dx,maxInfo.fio int 21h jc write_error mov cx,15 ; Запись участка доставки. jc write_error lea dx,maxInfo.dest int 21h jc write_error mov cx,15 ; Запись адреса. lea dx,maxInfo.address int 21h jc write_error mov cx,1 ; Запись количества lea dx,maxInfo.amount; выписанных изданий. int 21h jc write_error mov cx,256 ; Запись списка изданий. lea dx,maxInfo.list int 21h jc write_error mov dx,1 ret write_error: ; Печать ошибки на экран. mov ah,09h lea dx,writeError int 21h mov dx,0 ret writeMax endp
; Назначение: Осуществляет корректный выход из программы. doExit proc near mov ax,004Ch int 21h ret doExit endp ;----------------------------------------------------------- ; Данные. ;-----------------------------------------------------------
.data ifId dw ? ; Идентификатор входного файла. ofId dw ? ; Идентификатор выходного файла. ifName db 'C:\in.txt',0 ; Имя входного файла ofName db 'C:\out.txt',0 ; Имя выходного файла. maxPos db ? ; Позиция искомого подписчика в файле. lastPos dw ? ; Последняя позиция при считывании. openError db 'ERROR: Can not open the file!',0Ah,0Dh,'$' createError db 'ERROR: Can not create the file!',0Ah,0Dh,'$' readError db 'ERROR: Can not read data!',0Ah,0Dh,'$' writeError db 'ERROR: Can not write data!',0Ah,0Dh,'$'
INFO struc ; Структура: Информация о подписчике. fio db 20 DUP(?) dest db 15 DUP(?) address db 15 DUP(?) amount db ? list db 256 DUP(?) INFO ends
anInfo INFO <0,0,0,0,0> ; Инициализация структуры. maxInfo INFO <0,0,0,0,0> ; Данные искомого подписчика.
;----------------------------------------------------------- ; Конец программы. ;----------------------------------------------------------- end _main
|
Многое пробовал на asm чисто для ознакомления, да и на звание гуру asm тоже не претендую. =) Поэтому строго не судите, плиз. --- В коде я отметил, что возникает проблема при вызове функции DOS 3Fh, которая должна осуществлять чтение из устройства или файла. Файл открыт, дескриптор есть (в дебагере отлаживал - вроде все ОК с этим), но при вызове 3Fh в AX возвращается 05h, что означает: "Доступ запрещен". Подскажите, пожалуйста, в чем проблема и какой там доступ должен быть. Ибо входной файл 'C:\in.txt' присутсвует, он открывается, все права (на чтение, на запись) есть. Не пойму, в чем проблема. --- P.S.: Для запуска использую эмулятор DOS-BOX и DOS Emulator. Все это под линуксом, под виндой DOS-BOX криво работает почему-то. Отладчик стандартный - Turbo Debuger. Заранее спасибо. |