Консультация № 178239
06.05.2010, 17:56
45.00 руб.
0 16 1
Здравствуйте уважаемые эксперты!
У меня возникло затруднение - надо написать программу на TASM которая объединяет слова, начинающиеся на один и тот же символ и дописывает его (объединенное слово) в конец строки и вывести их на экран. слова вводить с клавиатуры.
вот пример:
waba wordos absen ; вводим исходную строку
waba wordos absen wabawordos ; то что должно получиться
дополнительные сведения: работа на TASM - 92 для процессора 8086 под дос (командная строка в windows xp)
предназначена для учебы.
и пожалуйста напишите программу наиболее простым способом (понятным для студента ) и прокомментируйте строчки я в ассемблере не очень силен
P.S. а можно будет задавать вопросы по пришлённому ответу если в нем что то неясно будет ?
Большое спасибо!

Обсуждение

давно
Старший Модератор
31795
6196
06.05.2010, 18:16
общий
Timmy:
Цитата: 245680
TASM - 92

Что это?
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
06.05.2010, 22:29
общий
tasm версия 92-го года кажется)
Неизвестный
06.05.2010, 23:11
общий
это ответ
Здравствуйте, Timmy.

В приложении приведен мой вариант решения Вашей задачи. Схема такая: читаем строку с клавиатуры в буфер, разбиваем ее на слова. Слова могут быть разделены одним или несколькими символами, перечисленными в массиве delimiters, так что Вы легко можете изменить разделители. Для каждого слова сохраняется его длина и указатель на начало. Предполагается, что слов не более 20, при превышении выводится сообщение и программа прекращает работу.
После заполнения массивов программа в цикле проверяет первые буквы слов на совпадение и дополняет входной буфер объединенными словами. В конце выводится результат.

Программа должна компилироваться в COM-файл.

Если есть вопросы, то обращайтесь в мини-форум, постараюсь помочь.

Успехов!

Приложение:
; Вопрос № 178239
; написать программу, которая объединяет слова, начинающиеся на один и
; тот же символ и дописывает его (объединенное слово) в конец строки.
; Результат вывести на экран. Слова вводить с клавиатуры.
; Пример:
; waba wordos absen ; вводим исходную строку
; waba wordos absen wabawordos ; то что должно получиться
; Платформа Dos, ассемблер TASM, процессор 8086

locals @@
model tiny

.data

bufInput db 120 ; размер буфера
lenInput db 0 ; длина введенной строки
szInput db 260 dup(0) ; сюда будет помещена введенная строка,
; а затем к концу будут присоединены объединенные слова (260=120*2+20)

delimiters db 9, ' .,;:!?-' ; массив разделителей слов
LEN_DELIM equ $-delimiters

MAX_WORDS equ 20 ; разрешаем до 20-ти слов
wordptr dw MAX_WORDS dup(0) ; массив указателей на слова
wordlen dw MAX_WORDS dup(0) ; массив длин слов
b_out db MAX_WORDS dup(0) ; массив признаков "слово выведено"
endptr dw 0 ; указатель на конец строки

msgEnterWords db 13,10,'Enter several words: $'
msgLimit db 13,10,'Too many words in the input string.$'
msgNoWords db 13,10,'No words found in the input string.$'
msgExit db 13,10,10, 'Press any key to exit...$'
msgResult db 13,10,'Result:',13,10,'$'

.code
.startup
; выводим пригласительное сообщение
mov ah,9 ; вывод строки на экран
mov dx,offset msgEnterWords ; смещение выводимой строки
int 21h

mov ah,0Ah ; ввод строки с клавиатуры
mov dx,offset bufInput ; смещение буфера
int 21h

; в цикле разбираем строку и сохраняем указатели на начала слов и длины слов
xor bx,bx ; счетчик слов * 2
xor ch,ch
mov cl,[lenInput] ; CX = длина введенной строки
jcxz @@no_words
cld
mov si,offset szInput ; смещение введенной строки
@@next:
call skip_delimiters ; пропускаем разделители
jcxz @@str_end
cmp bx,MAX_WORDS*2 ; проверяем на переполнение
jb @@ok
mov dx,offset msgLimit ; слишком много слов
jmp short @@msg
@@ok:
mov dx,si
dec dx
mov [wordptr][bx],dx ; сохраняем указатель на слово

call read_word ; пропускаем слово
mov ax,si
sub ax,dx
mov [wordlen][bx],ax ; сохраняем длину слова
inc bx ; увеличиваем счетчик (*2)
inc bx
test cx,cx ; здесь нельзя использовать loop, поскольку CX
jnz @@next ; может быть равно 0. Кроме того, здесь нельзя уменьшать CX.

@@str_end:
; обработка и вывод результатов

test bx,bx ; вообще, хоть одно слово было введено?
jnz @@process
@@no_words:
mov dx,offset msgNoWords ; смещение выводимой строки
@@msg:
mov ah,9 ; вывод строки на экран
jmp short @@exit

@@process:
lea bp,[wordptr-2][bx] ; BP = указатель на конец заполненной части массива
mov [endptr],si ; текущий указатель на конец строки

; Приблизительный алгоритм поиска:
; for( i=0; i < wordcount-1; ++i )
; if( i-тое слово не было выведено ) {
; for( j=i+1; j < wordcount; ++j )
; if( wordptr[i][0] == wordptr[j][0] )
; присоединить слово
; }

mov si,offset wordptr ; SI = адрес wordptr[i] (i=0)
mov bx,offset b_out

@@loop_i:
cmp byte ptr [bx],0 ; очередное слово уже было выведено?
jnz @@next_i ; да, идем к следующему
; нет, сканируем остаток

push bx ; сохраняем адрес b_out[i]
mov dx,bx
mov ah,0 ; флаг: i-тое слово еще не добавлено в конец
mov bx,[si] ; BX = указатель на начало слова[i]
mov al,[bx] ; первый символ слова

mov di,si ; DI = адрес wordptr[j]
inc di
inc di
inc dx ; DX = адрес b_out[j]
@@loop_j:
mov bx,[di] ; BX = указатель на начало слова [j]
cmp al,[bx] ; сравниваем первые символы слов
jne @@next_j ; они не будут равны, если j-тое слово уже было добавлено

test ah,ah ; i-тое слово уже было добавлено?
jne @@out
; нет, добавляем
push di
push si
mov di,[endptr] ; указатель на конец строки
mov byte ptr [di],' ' ; не забудем про разделитель
inc di
mov cx,[si+MAX_WORDS*2] ; длина i-того слова
mov si,[si] ; адрес i-того слова
rep movsb ; копируем слово
mov [endptr],di ; сохраняем указатель на конец строки
pop si
pop di
mov ah,1 ; i-тое слово добавлено в строку

@@out: ; добавляем j-тое слово
push di
push si
mov cx,[di+MAX_WORDS*2]
mov si,[di]
mov di,[endptr]
rep movsb
mov [endptr],di
pop si
pop di

mov bx,dx
mov byte ptr [bx],1 ; устанавливаем флаг: j-тое слово использовано
@@next_j:
inc dx ; продвигаем указатели, соответствующие j
inc di
inc di
cmp di,bp
jbe @@loop_j

pop bx ; адрес b_out[i]
@@next_i:
inc bx ; продвигаем указатели, соответствующие i
inc si
inc si
cmp si,bp
jb @@loop_i

; выводим результат
mov bx,[endptr]
mov byte ptr [bx],'$' ; помещаем терминатор строки '$' в буфер

mov ah,9 ; вывод строки на экран
mov dx,offset msgResult ; переходим на новую строку
int 21h
mov dx,offset szInput ; смещение нашей результирующей строки
@@exit:
int 21h

mov dx,offset msgExit ; смещение выводимой строки
int 21h

xor ah,ah ; ждем нажатия любой клавиши (чтобы увидеть результат,
int 16h ; прежде чем консоль в Windows закроется)

mov ax,4C00h ; завершаем программу с нулевым кодом
int 21h

; пропуск разделителей
skip_delimiters proc
@@next:
push cx
lodsb
mov cx,LEN_DELIM
mov di,offset delimiters
repne scasb
pop cx
loope @@next ; пропускаем разделители
ret
skip_delimiters endp

; пропуск слова
read_word proc
@@next:
push cx
lodsb
mov cx,LEN_DELIM
mov di,offset delimiters
repne scasb
pop cx
loopne @@next ; пропускаем, пока не встретим разделитель
jne @@ret ; эта проверка важна для последнего введенного слова
dec si
inc cx
@@ret: ret
read_word endp

end
Неизвестный
07.05.2010, 18:56
общий
есть вопросы начну по порядку:
1. что значит а)locals @@ ? б)LEN_DELIM equ $-delimiters
и вообще что означают две собаки @@ ? (нигде не нашел)
например тут:
jcxz @@no_words
или перед циклом: @@next:
в) что значит это: MAX_WORDS*2 ?
г) что значит запись типа mov [wordlen][bx],ax (что то пересылаем из ах.. куда и как ? )
2. что делает каждый цикл и процедура? можно пояснить коротким ответом к примеру:
@@next: разбираем строку и сохраняем указатели на начала слов и длины слов
@@next: ?
@@ok: ?
@@str_end: ?
........
read_word proc
2а) i-e и j-e слово это какое ?
3. у нас есть несколько меток с одним именем к примеру @@next. как определить к какой из них идет переход ? loopne @@next
4. Пожалуйста если можно максимально упростите программу написана она у вас сложно (для меня). для этого можно убрать кое - что "лишнее", например проверки и дополнения:
-при превышении выводится сообщение и программа прекращает работу
-разрешаем до 20-ти слов
-No words found in the input string
-Press any key to exit
-Too many words in the input string
-вообще, хоть одно слово было введено?
-пропуск разделителей и слова (если это нужно то зачем?)
Это надо убрать из всей программы где на них что то ссылается или проверяется (может не все условия и дополнения перечислил)
5. Если не сложно можно поправить программу под ехе (если это не усложнит ее) и убрать везде @@ ?(вопрос может не очень уместен т.к. не знаю что означают @@ )
и пожалуйста опишите поподробнее алгоритм работы программы в целом (с указанием на номер строчки кода или отрывок из кода) по комментариям понятны только отдельные моменты
Извиняюсь что столько много вопросов просто мне надо сдать эту программу полностью все разобрав
давно
Старший Модератор
31795
6196
07.05.2010, 20:08
общий
1)
Все метки начинающиеся с @@ обьявляются локальними и работать будут только в том логическом блоке программы, которому они определены.
Т.е. в основном теле программы и/или в процедурах(определенных как: read_word proc read_word endp). Такая возможность была введена, когда программы писались несколькими программистами, причем - каждый свою функцию или процедуру, а для меток использовали читаемые слова определяющие некоторые действия, для наглядности.
Если Вы уберете строчку locals @@, то получите такие ошибки:
**Error** q178239.ASM(186) Symbol already defined elsewhere: @@NEXT
**Error** q178239.ASM(193) Relative jump out of range by 004Bh bytes
**Error** q178239.ASM(199) Symbol already defined elsewhere: @@NEXT
**Error** q178239.ASM(206) Relative jump out of range by 0059h bytes

Повторное определенияе меток и превышение длины возможного перехода. Не обращайте на это внимание, просто считайте, что перед началом каждой метки в процедурах вместо двух собак добавляется имя самой процедуры, тем самым делается уникальным для всего кода.

LEN_DELIM equ $-delimiters
компилятор при создании программы выделяет память под код и данные. Такая запись значит, что от текущего адреса $ вычесть адрес метки delimiters и присвоить это значение переменной LEN_DELIM. Но давайте посмотрим что там у нас
delimiters db 9, ' .,;:!?-'
, набор разделителей слов, если Вы при компиляции включите листинг, то получите прибизительно такое содержание
20 0106 09 20 2E 2C 3B 3A 21+ delimiters db 9, ' .,;:!?-' ; массив разделителей слов
21 3F 2D
22 = 0009 LEN_DELIM equ $-delimiters

Посчитайте выделенные красным цветом байты, а теперь представьте, сколько нужно времени чтобы посчитать количество символов в к примеру во всем этом тексте:
msgEnterWords db 13,10,'Enter several words: $'
msgLimit db 13,10,'Too many words in the input string.$'
msgNoWords db 13,10,'No words found in the input string.$'
msgExit db 13,10,10, 'Press any key to exit...$'
msgResult db 13,10,'Result:',13,10,'$'

А так выделили некоторый участок, одна строка и у Вас уже есть длина этого участка, помоему удобно, пусть компилятор сам считает, а мы программисты народ ленивый, да и с математикой туго. Ещё одно удобство от этого после добавления или удаления символа на контролируемом участке компилятор посчитает сам значение и везде где встречается LEN_DELIM подставит, нужное, иначе программисту нужно было бы вспоминать что ещё нужно изменить и где. Я же говорил, мы народ ленивый, да и с памятью проблемы.

MAX_WORDS*2= 40 компилятор сам всё считает, а программист только меняет значение MAX_WORDS = 20

Цитата: 245680
Если не сложно можно поправить программу под ехе

Это очень трудно, нужно поменять модель памяти, к примеру так model small;tiny
сорри за иронию.
Пока с этим разберитесь, дальше будет ещё.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
07.05.2010, 20:24
общий
Timmy:
Цитата: 245680
1. что значит а)locals @@

Это значит, что все метки, которые начинаются с @@ будут локальными, то есть их область видимости - между любыми двумя, не начинающимися с @@ (например, внутри процедур).

Цитата: 245680
б)LEN_DELIM equ $-delimiters

Это стандартная запись в ассемблере для получения размера переменной. $ - текущее смещение в сегменте. Из текущего смещения вычитается смещение переменной delimiters и результат обозначается как LEN_DELIM (константа).

Цитата: 245680
что значит это: MAX_WORDS*2

Да это самое и значит: MAX_WORDS (=20) умноженное на 2. Надо стараться использовать символические имена вместо числовых значений. Это выражение вычисляется на этапе ассемблирования.

Цитата: 245680
что значит запись типа mov [wordlen][bx],ax

К адресу переменной wordlen прибавляется значение в регистре BX, и по получившемуся адресу записывается значение регистра AX. Рассматривайте это как индексацию массива.

Цитата: 245680
i-e и j-e слово это какое ?

это отсылки к комментарию:
; Приблизительный алгоритм поиска:
; for( i=0; i < wordcount-1; ++i )
; if( i-тое слово не было выведено ) {
; for( j=i+1; j < wordcount; ++j )
; if( wordptr[i][0] == wordptr[j][0] )
; присоединить слово
; }

Цитата: 245680
3. у нас есть несколько меток с одним именем к примеру @@next. как определить к какой из них идет переход ?

см. п.1, локальные метки

Цитата: 245680
4. Пожалуйста если можно максимально упростите программу написана она у вас сложно (для меня). для этого можно убрать кое - что "лишнее"

Извините, с моей точки зрения, в программе нет ничего лишнего. Все эти проверки нужны, чтобы программа работала корректно. Как Вы будете объясняться с преподавателем, если программа выполнит неверную операцию, зависнет или выдаст абракадабру?

Цитата: 245680
-пропуск разделителей и слова (если это нужно то зачем?)

Это совершенно необходимо! Вам надо выделить из входной строки отдельные слова!

Цитата: 245680
5. Если не сложно можно поправить программу под ехе (если это не усложнит ее)

Замените model tiny на model small
добавьте после .startup
mov ax,@data
mov ES,ax ; DS установлен директивой .startup
Это все. Программу можно будет скомпилировать в EXE.
А для того, чтобы получить com-файл из оригинального исходника, достаточно запустить tlink /t

Цитата: 245680
убрать везде @@

надеюсь, объяснение в п.1 достаточно понятно.

Ваши вопросы меня шокируют. Складывается впечатление, что Вы совершенно не знаете ассемблер.
давно
Старший Модератор
31795
6196
07.05.2010, 20:58
общий
amnick:
Цитата: 307758
Складывается впечатление, что Вы совершенно не знаете ассемблер.
Конечно не знает, т.к.
Цитата: 245680
Извиняюсь что столько много вопросов просто мне надо сдать эту программу полностью все разобрав
Спрашивающий ещё только учится и всех тонкостей не усвоил. Сами вопросы нормальные, ему нужно понять, что он будет сдавать. Просто у каждого из программистов есть свой стиль и свои наработанные трюки. Пару лет назад отвечая на вопросы получил в "личку" Спасибо, что ответили с кодом Ваших программ, я разбираюсь быстрее и всё понятно".

Просто в ответах учитывайте и такие моменты.
Удачи!
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
07.05.2010, 21:35
общий
Складывается впечатление, что Вы совершенно не знаете ассемблер.

Я ж грил у меня с ним туго Я знаю ассемблер только на уровне вывода сообщения "hello word!" (ну может чуть больше)
с моей точки зрения, в программе нет ничего лишнего

и все же упростите ну хотя б покажите какие строчки закомментировать.
и еще вопрос почему мы счетчик два раза инкрементируем ?
inc (si) | inc (di) | inc(bx)
inc (si) | inc(di) | inc(bx)
Опишите пожалста поподробнее словесно алгоритм работы программы от начала и до конца
особенно вот:
@@out (до следущей метки next j)
skip_delimiters proc
спасибо!
давно
Старший Модератор
31795
6196
07.05.2010, 21:39
общий
amnick:
Цитата: 307758
mov ax,@data
mov ES,ax ; DS установлен директивой .startup

Это не спасает от предупреждения линковщика

Об отсутсвии стека.
И ещё .startup сама все делает:
1 55 0000 @Startup:
1 56 0000 BA 0000s MOV DX,@data
1 57 0003 8E DA MOV DS,DX
1 58 0005 8C D3 MOV BX,SS
1 59 0007 2B DA SUB BX,DX
1 60 0009 D1 E3 SHL BX,1
1 61 000B D1 E3 SHL BX,1
1 62 000D D1 E3 SHL BX,1
1 63 000F D1 E3 SHL BX,1
1 64 0011 FA CLI
1 65 0012 8E D2 MOV SS,DX
1 66 0014 03 E3 ADD SP,BX
1 67 0016 FB STI

64 и 67 строчки правда лишние, но пусть будут.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Старший Модератор
31795
6196
07.05.2010, 21:50
общий
Timmy:
Цитата: 245680
[q=245680]и еще вопрос почему мы счетчик два раза инкрементируем ?
inc (si) | inc (di) | inc(bx)
inc (si) | inc(di) | inc(bx)

В программе используются массивы слов, а это два байта.
Включите листинг, см. рисунок выше и посмотрите на то как распределяется память, т.е. считаются адреса после директив компилятора DB и DW
У меня компилятор дал следующее:
Код:
      1				     ; Вопрос №	178239
2 ; написать программу, которая объединяет слова, начинающиеся на один и
3 ; тот же символ и дописывает его (объединенное слово) в конец строки.
4 ; Результат вывести на экран. Слова вводить с клавиатуры.
5 ; Пример:
6 ; waba wordos absen ; вводим исходную строку
7 ; waba wordos absen wabawordos ; то что должно получиться
8 ; Платформа Dos, ассемблер TASM, процессор 8086
9
10 locals @@
11 0000 model small;tiny
12
13 0000 .data
14
15 0000 78 bufInput db 120 ; размер буфера
16 0001 00 lenInput db 0 ; длина введенной строки
17 0002 0104*(00) szInput db 260 dup(0) ; сюда будет помещена введенная строка,
18 ; а затем к концу будут присоединены объединенные слова (260=120*2+20)
19
20 0106 09 20 2E 2C 3B 3A 21+ delimiters db 9, ' .,;:!?-' ; массив разделителей слов
21 3F 2D
22 = 0009 LEN_DELIM equ $-delimiters
23
24 = 0014 MAX_WORDS equ 20 ; разрешаем до 20-ти слов
25 010F 14*(0000) wordptr dw MAX_WORDS dup(0) ; массив указателей на слова
26 0137 14*(0000) wordlen dw MAX_WORDS dup(0) ; массив длин слов
27 015F 14*(00) b_out db MAX_WORDS dup(0) ; массив признаков "слово выведено"
28 0173 0000 endptr dw 0 ; указатель на конец строки
29
30 0175 0D 0A 45 6E 74 65 72+ msgEnterWords db 13,10,'Enter several words: $'
31 20 73 65 76 65 72 61+
32 6C 20 77 6F 72 64 73+
33 3A 20 24
34 018D 0D 0A 54 6F 6F 20 6D+ msgLimit db 13,10,'Too many words in the input string.$'
35 61 6E 79 20 77 6F 72+
36 64 73 20 69 6E 20 74+
37 68 65 20 69 6E 70 75+
38 74 20 73 74 72 69 6E+
39 67 2E 24
40 01B3 0D 0A 4E 6F 20 77 6F+ msgNoWords db 13,10,'No words found in the input string.$'
41 72 64 73 20 66 6F 75+
42 6E 64 20 69 6E 20 74+
43 68 65 20 69 6E 70 75+
44 74 20 73 74 72 69 6E+
45 67 2E 24
46 01D9 0D 0A 0A 50 72 65 73+ msgExit db 13,10,10, 'Press any key to exit...$'
47 73 20 61 6E 79 20 6B+
48 65 79 20 74 6F 20 65+
49 78 69 74 2E 2E 2E 24
50 01F5 0D 0A 52 65 73 75 6C+ msgResult db 13,10,'Result:',13,10,'$'
51 74 3A 0D 0A 24
52
53 0201 .code
54 .startup
1 55 0000 @Startup:
1 56 0000 BA 0000s MOV DX,@data
1 57 0003 8E DA MOV DS,DX
1 58 0005 8C D3 MOV BX,SS
1 59 0007 2B DA SUB BX,DX
1 60 0009 D1 E3 SHL BX,1
1 61 000B D1 E3 SHL BX,1
1 62 000D D1 E3 SHL BX,1
1 63 000F D1 E3 SHL BX,1
1 64 0011 FA CLI
1 65 0012 8E D2 MOV SS,DX
1 66 0014 03 E3 ADD SP,BX
1 67 0016 FB STI
68 ; выводим пригласительное сообщение
69 0017 B4 09 mov ah,9 ; вывод строки на экран
70 0019 BA 0175r mov dx,offset msgEnterWords ; смещение выводимой строки
71 001C CD 21 int 21h
72
73 001E B4 0A mov ah,0Ah ; ввод строки с клавиатуры
74 0020 BA 0000r mov dx,offset bufInput ; смещение буфера
75 0023 CD 21 int 21h
76
77 ; в цикле разбираем строку и сохраняем указатели на начала слов и длины слов
78 0025 33 DB xor bx,bx ; счетчик слов * 2
79 0027 32 ED xor ch,ch
80 0029 8A 0E 0001r mov cl,[lenInput] ; CX = длина введенной строки
81 002D E3 2F jcxz @@no_words
82 002F FC cld
83 0030 BE 0002r mov si,offset szInput ; смещение введенной строки
84 0033 @@next:
85 0033 E8 00BB call skip_delimiters ; пропускаем разделители
86 0036 E3 22 jcxz @@str_end
87 0038 83 FB 28 cmp bx,MAX_WORDS*2 ; проверяем на переполнение
88 003B 72 05 jb @@ok
89 003D BA 018Dr mov dx,offset msgLimit ; слишком много слов
90 0040 EB 1F jmp short @@msg
91 0042 @@ok:
92 0042 8B D6 mov dx,si
93 0044 4A dec dx
94 0045 89 97 010Fr mov [wordptr][bx],dx ; сохраняем указатель на слово
95
96 0049 E8 00B3 call read_word ; пропускаем слово
97 004C 8B C6 mov ax,si
98 004E 2B C2 sub ax,dx
99 0050 89 87 0137r mov [wordlen][bx],ax ; сохраняем длину слова
100 0054 43 inc bx ; увеличиваем счетчик (*2)
101 0055 43 inc bx
102 0056 85 C9 test cx,cx ; здесь нельзя использовать loop, поскольку CX
103 0058 75 D9 jnz @@next ; может быть равно 0. Кроме того, здесь нельзя уменьшать CX.
104
105 005A @@str_end:
106 ; обработка и вывод результатов
107
108 005A 85 DB test bx,bx ; вообще, хоть одно слово было введено?
109 005C 75 07 jnz @@process
110 005E @@no_words:
111 005E BA 01B3r mov dx,offset msgNoWords ; смещение выводимой строки
112 0061 @@msg:
113 0061 B4 09 mov ah,9 ; вывод строки на экран
114 0063 EB 7C jmp short @@exit
115
116 0065 @@process:
117 0065 8D AF 010Dr lea bp,[wordptr-2][bx] ; BP = указатель на конец заполненной части массива
118 0069 89 36 0173r mov [endptr],si ; текущий указатель на конец строки
119
120 ; Приблизительный алгоритм поиска:
121 ; for( i=0; i < wordcount-1; ++i )
122 ; if( i-тое слово не было выведено ) {
123 ; for( j=i+1; j < wordcount; ++j )
124 ; if( wordptr[i][0] == wordptr[j][0] )
125 ; присоединить слово
126 ; }
127
128 006D BE 010Fr mov si,offset wordptr ; SI = адрес wordptr[i] (i=0)
129 0070 BB 015Fr mov bx,offset b_out
130
131 0073 @@loop_i:
132 0073 80 3F 00 cmp byte ptr [bx],0 ; очередное слово уже было выведено?
133 0076 75 51 jnz @@next_i ; да, идем к следующему
134 ; нет, сканируем остаток
135
136 0078 53 push bx ; сохраняем адрес b_out[i]
137 0079 8B D3 mov dx,bx
138 007B B4 00 mov ah,0 ; флаг: i-тое слово еще не добавлено в конец
139 007D 8B 1C mov bx,[si] ; BX = указатель на начало слова[i]
140 007F 8A 07 mov al,[bx] ; первый символ слова
141
142 0081 8B FE mov di,si ; DI = адрес wordptr[j]
143 0083 47 inc di
144 0084 47 inc di
145 0085 42 inc dx ; DX = адрес b_out[j]
146 0086 @@loop_j:
147 0086 8B 1D mov bx,[di] ; BX = указатель на начало слова [j]
148 0088 3A 07 cmp al,[bx] ; сравниваем первые символы слов
149 008A 75 35 jne @@next_j ; они не будут равны, если j-тое слово уже было +
150 добавлено
151
152 008C 84 E4 test ah,ah ; i-тое слово уже было добавлено?
153 008E 75 19 jne @@out
154 ; нет, добавляем
155 0090 57 push di
156 0091 56 push si
157 0092 8B 3E 0173r mov di,[endptr] ; указатель на конец строки
158 0096 C6 05 20 mov byte ptr [di],' ' ; не забудем про разделитель
159 0099 47 inc di
160 009A 8B 4C 28 mov cx,[si+MAX_WORDS*2] ; длина i-того слова
161 009D 8B 34 mov si,[si] ; адрес i-того слова
162 009F F3> A4 rep movsb ; копируем слово
163 00A1 89 3E 0173r mov [endptr],di ; сохраняем указатель на конец строки
164 00A5 5E pop si
165 00A6 5F pop di
166 00A7 B4 01 mov ah,1 ; i-тое слово добавлено в строку
167
168 00A9 @@out: ; добавляем j-тое слово
169 00A9 57 push di
170 00AA 56 push si
171 00AB 8B 4D 28 mov cx,[di+MAX_WORDS*2]
172 00AE 8B 35 mov si,[di]
173 00B0 8B 3E 0173r mov di,[endptr]
174 00B4 F3> A4 rep movsb
175 00B6 89 3E 0173r mov [endptr],di
176 00BA 5E pop si
177 00BB 5F pop di
178
179 00BC 8B DA mov bx,dx
180 00BE C6 07 01 mov byte ptr [bx],1 ; устанавливаем флаг: j-тое слово использовано
181 00C1 @@next_j:
182 00C1 42 inc dx ; продвигаем указатели, соответствующие j
183 00C2 47 inc di
184 00C3 47 inc di
185 00C4 3B FD cmp di,bp
186 00C6 76 BE jbe @@loop_j
187
188 00C8 5B pop bx ; адрес b_out[i]
189 00C9 @@next_i:
190 00C9 43 inc bx ; продвигаем указатели, соответствующие i
191 00CA 46 inc si
192 00CB 46 inc si
193 00CC 3B F5 cmp si,bp
194 00CE 72 A3 jb @@loop_i
195
196 ; выводим результат
197 00D0 8B 1E 0173r mov bx,[endptr]
198 00D4 C6 07 24 mov byte ptr [bx],'$' ; помещаем терминатор строки '$' в буфер
199
200 00D7 B4 09 mov ah,9 ; вывод строки на экран
201 00D9 BA 01F5r mov dx,offset msgResult ; переходим на новую строку
202 00DC CD 21 int 21h
203 00DE BA 0002r mov dx,offset szInput ; смещение нашей результирующей строки
204 00E1 @@exit:
205 00E1 CD 21 int 21h
206
207 00E3 BA 01D9r mov dx,offset msgExit ; смещение выводимой строки
208 00E6 CD 21 int 21h
209
210 00E8 32 E4 xor ah,ah ; ждем нажатия любой клавиши (чтобы увидеть результат,
211 00EA CD 16 int 16h ; прежде чем консоль в Windows закроется)
212
213 00EC B8 4C00 mov ax,4C00h ; завершаем программу с нулевым кодом
214 00EF CD 21 int 21h
215
216 ; пропуск разделителей
217 00F1 skip_delimiters proc
218 00F1 @@next:
219 00F1 51 push cx
220 00F2 AC lodsb
221 00F3 B9 0009 mov cx,LEN_DELIM
222 00F6 BF 0106r mov di,offset delimiters
223 00F9 F2> AE repne scasb
224 00FB 59 pop cx
225 00FC E1 F3 loope @@next ; пропускаем разделители
226 00FE C3 ret
227 00FF skip_delimiters endp
228
229 ; пропуск слова
230 00FF read_word proc
231 00FF @@next:
232 00FF 51 push cx
233 0100 AC lodsb
234 0101 B9 0009 mov cx,LEN_DELIM
235 0104 BF 0106r mov di,offset delimiters
236 0107 F2> AE repne scasb
237 0109 59 pop cx
238 010A E0 F3 loopne @@next ; пропускаем, пока не встретим разделитель
239 010C 75 02 jne @@ret ; эта проверка важна для последнего введенного слова
240 010E 4E dec si
241 010F 41 inc cx
242 0110 C3 @@ret: ret
243 0111 read_word endp
244
245 end
Symbol Name Type Value

??DATE Text "05/07/10"
??FILENAME Text "q178239 "
??TIME Text "20:17:16"
??VERSION Number 0200
@@EXIT Near _TEXT:00E1
@@LOOP_I Near _TEXT:0073
@@LOOP_J Near _TEXT:0086
@@MSG Near _TEXT:0061
@@NEXT Near _TEXT:00FF
@@NEXT Near _TEXT:0033
@@NEXT Near _TEXT:00F1
@@NEXT_I Near _TEXT:00C9
@@NEXT_J Near _TEXT:00C1
@@NO_WORDS Near _TEXT:005E
@@OK Near _TEXT:0042
@@OUT Near _TEXT:00A9
@@PROCESS Near _TEXT:0065
@@RET Near _TEXT:0110
@@STR_END Near _TEXT:005A
@CODE Text _TEXT
@CODESIZE Text 0
@CPU Text 0101H
@CURSEG Text _TEXT
@DATA Text DGROUP
@DATASIZE Text 0
@FILENAME Text Q178239
@MODEL Text 2
@STARTUP Near _TEXT:0000
@WORDSIZE Text 2
BUFINPUT Byte DGROUP:0000
B_OUT Byte DGROUP:015F
DELIMITERS Byte DGROUP:0106
ENDPTR Word DGROUP:0173
LENINPUT Byte DGROUP:0001
LEN_DELIM Number 0009
MAX_WORDS Number 0014
MSGENTERWORDS Byte DGROUP:0175
MSGEXIT Byte DGROUP:01D9
MSGLIMIT Byte DGROUP:018D
MSGNOWORDS Byte DGROUP:01B3
MSGRESULT Byte DGROUP:01F5
READ_WORD Near _TEXT:00FF
SKIP_DELIMITERS Near _TEXT:00F1
SZINPUT Byte DGROUP:0002
WORDLEN Word DGROUP:0137
WORDPTR Word DGROUP:010F
Groups & Segments Bit Size Align Combine Class
DGROUP Group
_DATA 16 0201 Word Public DATA
_TEXT 16 0111 Word Public CODE
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
07.05.2010, 21:50
общий
Цитата: Зенченко Константин Николаевич
Это не спасает от предупреждения линковщика
Об отсутсвии стека.
И ещё .startup сама все делает:


Ну так программа изначально делалась как COM. Эта модификация - по запросу, чтобы получить рабочий EXE.
А .startup сама все не делает. Я тоже смотрел листинг перед тем ответом и обратил внимание, что ES не устанавливается. А программа использует ES, как Вы понимаете, в инструкциях movsb. Так что ES тоже нужно инициализировать.
Неизвестный
07.05.2010, 22:10
общий
Timmy:
Рассмотрим несколько фрагментов:
Код:
; в цикле разбираем строку и сохраняем указатели на начала слов и длины слов
xor bx,bx ; счетчик слов * 2
xor ch,ch
mov cl,[lenInput] ; CX = длина введенной строки
jcxz @@no_words

Если здесь убрать jcxz @@no_words, то при вводе пустой строки будет ошибка: программа войдет в цикл loop (при первом вызове процедуры skip_delimiters) при CX=0. Значит, цикл выполнится 65536 раз. Это стандартная проверка перед циклом loop! Ее нельзя убирать.

Код:
@@next:
call skip_delimiters ; пропускаем разделители
jcxz @@str_end
cmp bx,MAX_WORDS*2 ; проверяем на переполнение
jb @@ok
mov dx,offset msgLimit ; слишком много слов
jmp short @@msg
@@ok:

jcxz @@str_end - достигли конца строки (введенная строка кончалась разделителями слов, например, точкой). Проверка совершенно необходима, см. выше.
Далее, проверка на переполнение. Тоже обязательна - причина, по-моему, очевидна - если введено больше MAX_WORDS слов, то память будет испорчена и результат работы программы непредсказуем. Почему вставлена именно здесь: мы прошли предыдущую проверку jcxz @@str_end, она показала, что строка еще не закончена. Значит, следующий символ - начало нового слова. Но весь массив уже заполнен, помещать информацию (смещение и длину) нового слова уже некуда.

Код:
; обработка и вывод результатов
test bx,bx ; вообще, хоть одно слово было введено?
jnz @@process
@@no_words:

Эта проверка очень простая, зато позволяет избежать выполнения длинного (относительно) куска кода. Причем, проверки в следующих вложенных циклах проводятся в конце, поэтому необходима проверка перед входом в циклы, чтобы пропустить их, если делать ничего не нужно.

Код:
	xor	ah,ah	; ждем нажатия любой клавиши (чтобы увидеть результат,
int 16h ; прежде чем консоль в Windows закроется)

Ну, тот уже все объяснено. функция 0 прерывания 16h - ожидание нажатия клавиши.

В общем, как я уже писал, с моей точки зрения, удалять там нечего - если только Вы не хотите нарваться на неприятности.
давно
Старший Модератор
31795
6196
07.05.2010, 22:33
общий
amnick:
Сорри. Мне показалось Вы SS настраивали.
Код смотрел только мельком.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Старший Модератор
31795
6196
07.05.2010, 22:46
общий
Timmy:
Я согласен с экспертом amnick, там больше нечего упрощать. В коде все только самое нужное.

Я бы ещё парочку проверок добавил.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
09.05.2010, 00:56
общий
там больше нечего упрощать.

эх.
Ну ладно ок.
Последняя просьба если несложно oпишите пожалуйста поподробнее словесно алгоритм работы программы от начала и до конца.
И еще: тут написано что до закрытия вопроса осталось 2 суток.
т.е. после 2 суток я не смогу здесь больше ничего спрашивать?
просто сдавать буду только через 3 дня и может возникнуть еще какой нить вопрос по программе
давно
Старший Модератор
31795
6196
12.05.2010, 14:01
общий
Timmy:
Цитата: 245680
т.е. после 2 суток я не смогу здесь больше ничего спрашивать?


Сам вопрос ушел в рассылку, но мини-форум вопроса остается открытым, для всех зарегистрированных участников.


Алгоритм простой:
- Вводится строка функцией

- Разбор строки:
call skip_delimiters - пропускает все символы-разделители, другими словами, находит адрес начала слова в памяти и он запоминается в массиве указателей mov [ wordptr][ bx],dx - используется для поиска нужных слов и копирования.
call read_word - пропускает слово и считает его длину, которая потом запоминается в массиве mov [ wordlen][ bx],ax - используется при копированиии нужных слов.
так продолжется пока не будет проверена вся строка.

- Формирование результа:
из массива указателей считывается адреса начала первого и очередного слова, если первые буквы у них совпадают, то в массиве b_out данное слово отмечается, все отмеченные слова объединяются и дописываются(копируются ) в конец строки строковой командой rep movsb.

- Вывод результата.
Вот собственно и всё.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Форма ответа