;Задание 6, вариант 12. ;Выдать все строки с номерами 21 и 55 ;Выдать все строки с символом " .MODEL TINY ;Будем использовать формат исполняемого файла COM .CODE ;Сегмент кода (он же в нашем случае и сегмент данных и стека одновременно) ORG 100h ;Программный счетчик ставим на 100h, это требуется для COM файлов. Begin: ;По началу из-за особенностей COM файлов у нас CS = SS = DS = ES. ;То есть мы будем использовать один сегмент для всего. Стек при этом растет с конца JMP Main ;Переходим на стартовую метку программы ReqNum1 EQU 21 ;Требуемые номера ReqNum2 EQU 55 ReqChar = """" ;требуемый символ. Повторен 2 раза из-за экранирования. ;Т.к помещаем двойные кавычки в двойные кавычки. ;Далее текстовый строки. Для удобства форматирования вывода могут содержать ;в себе символы новой строки и перевода каретки. NotFoundNum1 DB "Строка с номером 21 не найдена", 13, 10, "$" NotFoundNum2 DB "Строка с номером 55 не найдена", 13, 10, "$" NotFoundChar DB "Строка, содержащая символ "" не найдена", 13, 10, "$" StringN DB "Строка с символом "": $" StringNum1 DB "Строка #21: $" StringNum2 DB "Строка #55: $" CRLF DB 13, 10, "$" StrI DB 1 ;Текущий номер строки LastFlag DB 0 ;Была ли найденная строка последней? StartPosition DW 0 ;Позиция и длина последней найденной строки StartLength DW 0 Num1Out DB 0 ;Флаги. Хранят статус факта вывода строк Num2Out DB 0 CharOut DB 0 LaunchString DB "Строка запуска программы # : $" ;Начало сегмента кода Main: ;Главная функция. С нее начинается работа программы MOV AX,[ES:2Ch] ;AX - сегмент переменных среды MOV ES,AX ;ES теперь - сегмент переменных среды CheckLoop: CALL GetString ;Получаем строку CALL CheckStrForSymbols ;Проверяем ее на наличие требуемых символов CALL CheckNumber ;Проверяем строку по номерам (нужно ли выводить) INC BYTE PTR [StrI] ;Переходим к следующей строке CMP BYTE PTR [LastFlag],1 ;Если строки еще остались - прочитаем еще JNE CheckLoop CMP BYTE PTR [Num1Out],1 ;Если первая строка не была выведена JE CheckNext MOV DX,OFFSET NotFoundNum1 ;То доложим об этом, как требует условие CALL Print CheckNext: CMP BYTE PTR [Num2Out],1 ;Тоже самое и для второй строки JE SkipIt MOV DX,OFFSET NotFoundNum2 CALL Print SkipIt: CMP BYTE PTR [CharOut],0 ;И тоже самое для строки с символом JNZ Done MOV DX,OFFSET NotFoundChar ;Выведем сообщение, если не находили таких строк CALL Print Done: ;Теперь остается вывести строку запуска программы и ее номер. ;Сейчас DI практически на нее указывает, не считая 3 байт. MOV SI,OFFSET LaunchString + 28 ;SI будет указывать ;Чтобы получить строку номера - придется делить: MOV AL,[StrI] ;AL - номер строки MOV BL,10 ;Делить будет на 10 DivLoop: XOR AH,AH ;Обнулим остаток DIV BL ;Делим. В AH попадет остаток от деления MOV BYTE PTR [SI],AH ;Записываем его в строку ADD BYTE PTR [SI],"0" ;И превращаем в символ числа DEC SI ;К предыдущему символу TEST AL,AL ;Продолжаем, пока результат не 0 JNZ DivLoop MOV DX,OFFSET LaunchString ;Выведем комментарий к строке вместе с номером CALL Print ADD DI,3 ;Заставим DI указывать на строку запуска CALL GetString ;И отобразим ее тоже. CALL Output MOV AX,4C00h ;Завершаем программу и выходим в DOS INT 21h ;Проверяет, какой номер у полученной строки. Если его нужно вывести - выводит CheckNumber: MOV AL,ReqNum1 ;AL - первый из номеров итнересующих нас строк CMP BYTE PTR [StrI],AL ;Индекс текущей строки совпадает с ним? JNE CheckSecond ;нет - переходим к следующей MOV DX,OFFSET StringNum1 ;Иначе выводим сообщение, что строка найдена CALL Print MOV DI,[StartPosition] ;И саму строку MOV CX,[StartLength] CALL Output MOV DX,OFFSET CRLF CALL Print MOV BYTE PTR [Num1Out],1 INC DI ;Вывести первую строку CheckSecond: MOV AL,ReqNum2 ;Аналогично и для второй строки CMP BYTE PTR [StrI],AL JNE NotThisTime MOV DX,OFFSET StringNum2 CALL Print MOV DI,[StartPosition] MOV CX,[StartLength] CALL Output MOV DX,OFFSET CRLF CALL Print MOV BYTE PTR [Num2Out],1 INC DI NotThisTime: RET ;Проверяет, имеются ли в полученной строке искомый нами символ. ;Если да - выводит такую строку ;Предполагает, что на входе CX - длина строки, DI - ее начало CheckStrForSymbols: MOV [StartPosition],DI ;Сохраняем в переменные начало строки и ее длину MOV [StartLength],CX MOV AH,ReqChar ;Ah - код интеерсующего нас символа NewChar: CMP [ES:DI],AH ;Является ли текущий символ строки искомым? JE Found ;Если да, перейдем к выводу строки INC DI ;Иначе проверим следующий символ DEC CX JNZ NewChar ;Пока строка не кончилась JMP NotFound Found: MOV BYTE PTR [CharOut],1 ;Ставим флаг, что нашли хоть одну строку с указанным символом MOV DX,OFFSET StringN ;И выводим эту строку с комментарием CALL Print MOV DI,[StartPosition] MOV CX,[StartLength] CALL Output MOV DX,OFFSET CRLF CALL Print INC DI RET NotFound: INC DI RET ;Получает очередную строку из сегмента переменных среды. ;Предполагает, что ES:DI - сегмент переменных среды ;Устанавливает LastFlag в 1, если полученная строка была последней. Иначе 0. ;На выходе DI - начало строки. CX - ее длина. GetString: PUSH DI MOV CX,65535 ;Ведем поиск по всему сегменту XOR AL,AL ;Ищем нули CLD ;Направление поиска на инкремент REPNE SCASB ;Ищем CMP BYTE PTR [ES:DI],0 ;Если сразу же после нуля есть еще один JE LastString ;то это была последняя строка POP DI ;Восстановим старое значение указателя на строку NEG CX ;И приведем в порядок CX, чтобы показывал длину найденной строки SUB CX,2 RET LastString: POP DI ;Аналогично и тут NEG CX SUB CX,2 MOV BYTE PTR [LastFlag],1 RET ;Выводит строку символов с помощью функции AH-02h прерывания INT 21h ;Начало строки - ES:DI. Символы выводятся в количестве CX штук Output: MOV AH,02h ;INT 21h, 02h - вывод символа StartLoop: MOV DL,[ES:DI] ;DL - код текущего символа строки INT 21h ;Выводим INC DI ;и переходим к следующему символу LOOP StartLoop ;Так, пока не отобразим все CX символов ExitLoop: RET Print: ;Просто функция вывода строки, чтобы MOV AH,09H ;писать не 3 строчки на вывод, а две. INT 21h RET