1. Заголовок или название темы должно быть информативным 2. Все тексты программ должны помещаться в теги [CODE=asm] [/CODE] 3. Прежде чем задавать вопрос, см. "FAQ",если там не нашли ответа, воспользуйтесь ПОИСКОМ, возможно, такую задачу уже решали! 4. Не предлагайте свои решения на других языках, кроме Ассемблера. Исключение только с согласия модератора. 5. НЕ используйте форум для личного общения! Все, что не относиться к обсуждению темы - на PM! 6. Проверяйте программы перед тем, как выложить их на форум!!
пишу программу вычисления интеграла функции 1/ln x методом трапеций, для начала взял n=20 (затем буду увеличивать). Файл .exe создан, но выдает ошибку и вылетает. При просмотре в дебаггере вижу, что еще на этапе вычисления шага в регистре ST0 оказывается отрицательная величина, еще через пару шагов вылетает. В чем тут дело?
Заранее спасибо.
.486p .model small .stack 100h .data a dw 2 b dw 5 x dq 2 n dw 20 h dq ? y dq ?
.code main proc mov ax, @data mov ds, ax finit fild b fisub a fidiv n fst h ;вычисляем шаг h
fld1 fild b fyl2x fldln2 fmul fld1 fdiv fADD y fild b fdivr st(1), st ;вычисляем (ln a + ln b)/2
cycl: fadd y fst y
fild x fadd h fst x ;производим приращение х и каждый раз сохраняем ficom b je mult_ ;пока не равно b(=5),продолжаем
fld x fyl2x fldln2 fmul fld1 fdiv ; loop cycl ;
mult_: fld y ; fld h ; fmul ;когда х достиг значения 5, перемножаем на h
exit: mov ax, 4c00h ; int 21h ; main endp end main
Снова загвоздка: не принимает директиву loop, попросту ее пропускает. Это свойство моей версии ассемблера или режима 286р?
P.S. Виноват: забыл на счетчик СХ поставить. Правда, он почему-то считает только два цикла, затем обнуляется. Притом во втором цикле в x грузит сразу 4,8 вместо 2,15. ... прилагаю новый листинг.
.286p .model small .stack 100h .data a dw 2 b dw 5 x dq 2 n dw 20 h dq ? y dq ?
.code main proc mov ax, @data mov ds, ax mov CX, [n] dec CX finit fild b fisub a fidiv n fst h ;вычисляем шаг h
finit fld1 fild b fyl2x fldln2 fmul ; вычиcляем ln 5 fld1 fdivr ;вычиcляем 1/ln 5 fstp y
fldln2 fld1 fdivr ; вычисляем 1/ln2 fadd y fstp y fild a fld y fdivr ;вычисляем (1/ln a + 1/ln b)/2 fstp y
cycl: finit fld x fadd h fst x ;производим приращение х и каждый раз сохраняем fld1 fld x fyl2x fldln2 fmul fld1 ; fdivr ; fadd y fstp y loop cycl ;
mult_: fld y fld h fmul ;когда х достиг значения 5, перемножаем на h
Это свойство моей версии ассемблера или режима 286р?
Это особенности цикла LOOP. Чтобы он работал, надо занести число повторений в CX. У тебя CX - нулевой. Похоже, тебе нужен не LOOP, а простой JMP, если ты из цикла уходишь по "je mult_"
Цитата
А почему всё-таки fild пятёрки загрузил 8397?
А кто его знает... В 286-ом режиме грузит нормально, выше - начинает придумывать что-то своё.
P.S. ТОчно, забыл на счетчик СХ поставить. Правда, он почему-то считает только два цикла, затем обнуляется. Притом во втором цикле в x грузит сразу 4,8 вместо 2,15. ... прилагаю новый листинг.
.286p .model small .stack 100h .data a dw 2 b dw 5 x dq 2 n dw 20 h dq ? y dq ?
.code main proc mov ax, @data mov ds, ax mov CX, [n] dec CX finit fild b fisub a fidiv n fst h ;вычисляем шаг h
finit fld1 fild b fyl2x fldln2 fmul ; вычиcляем ln 5 fld1 fdivr ;вычиcляем 1/ln 5 fstp y
fldln2 fld1 fdivr ; вычисляем 1/ln2 fadd y fstp y fild a fld y fdivr ;вычисляем (1/ln a + 1/ln b)/2 fstp y
cycl: finit fld x fadd h fst x ;производим приращение х и каждый раз сохраняем fld1 fld x fyl2x fldln2 fmul fld1 ; fdivr ; fadd y fstp y loop cycl ;
Опять вынужден обратиться: программу продолжил, но новый фрагмент он не видит и сразу переходит к выводу на экран прежнего значения для n=20. Привожу листинг без процедуры вывода на экран:
.286p .model small .stack 100h .data a dw 2 b dw 5 x dd 2.0 n dw 20 d dw 20 h dq ? y dq ? y2 dq ?
.code main proc mov ax, @data mov ds, ax m1: mov CX, [n] dec CX finit fild b fisub a fidiv n fst h ;вычисляем шаг h
finit fld1 fild b fyl2x fldln2 fmul ; вычиcляем ln 5 fld1 fdivr ;вычиcляем 1/ln 5 fild n ficom d jg m2 fxch fstp y
fldln2 fld1 fdivr ; вычисляем 1/ln2 fadd y fstp y fild a fld y fdivr ;вычисляем (1/ln a + 1/ln b)/2 fstp y
cycl: finit fld x fadd h fst x ;производим приращение х и каждый раз
сохраняем
fld1 fld x fyl2x fldln2 fmul fld1 ; fdivr ; fadd y fstp y loop cycl
mult_: fld y fld h fmul ;когда х = 5, перемножаем на h fst y
;повторяем то же для n=40 finit fild n fiadd d fistp n jmp m1 m2: fxch fstp y2
сработает так, как ты задумал? Не будет этого. Это тебе не процессорные команды сравнения, чтоб можно было использовать любые сравнения. Общий шаблон - такой:
fcomp x ; сравниваем верхушку стека с числом X fstsw ax ; сохраняем слово состояния сопроцессора в AX sahf ; заталкиваем AH в регистр флагов
; а теперь - внимание: jp lbl_1 ; значения несравнимы jc lbl_2 ; st(0) < x jz lbl_3 ; st(0) = x ; Раз мы пришли сюда - значит st(0) > x
А во-вторых - зачем тебе все эти пляски с прыжками из одной части кода в другую? Сделай 2 нормальных вложенных цикла, изначально N присвой 0, и тут же его увеличивай на D, это будет внешний цикл. А внутренний - вычисление самого интеграла. Программа упростится донельзя (а если еще воспользоваться макросредствами ассемблера - у тебя же TASM, я правильно понимаю?) то все будет еще проще.
Вот первоначальный вариант (можно еще дорабатывать)
.286p .model small .stack 100h .data a dw 2 b dw 5 x dq 2.0 n dw 0 d dw 20 h dq ? y dq ?
.code
CalcF macro ; На вершине стека должен быть параметр fld1 fld st(1) fyl2x fldln2 fmul fld1 fdivr
endm
iCalcF macro param fld1 fild param
fyl2x fldln2 fmul fld1 fdivr
endm
main proc
mov ax, @data mov ds, ax
; начинаем цикл по увеличению N mov cx, 3 ; Будут рассчитаны значения при N = 20, 40, 60 outer_loop: push cx
mov cx, n add cx, d mov n, cx dec cx
; вычисляем шаг finit fild b fisub a fidiv n fst h ; Сохраняем шаг
iCalcF b ; 1 / ln b fstp y iCalcF a ; 1 / ln a fadd y ; Складываем fdiv x ; Делим сумму на 2 fstp y ; Сохраняем начальное приближение
; Теперь внутренний цикл - вычисляем интеграл fld x ; Будем держать X в стеке сопроцессора inner_loop: fadd h ; X <- X + h CalcF fadd y fstp y ; Сохраняем измененное значение интеграла loop inner_loop
fld y fmul h ; Домножаем на h - получаем искомое значение call outfloat
pop cx loop outer_loop;
exit: mov ax, 4c00h ; int 21h ; main endp
; За кадром - реализация outfloat, распечатывающая содержимое ; вершины стека сопроцессора и выбрасывающая распечатанное ; значение из стека
Кстати, почему я организовал дополнительные циклы, и ввел переменную у2: необходимо найти решение с определенной точностью (0,0001). Для этого придется сравнивать значения F для каждых "соседних" n. Соответственно, если для каждого последующего n я записываю значения f в одну и ту же переменную у, то значения при меньшем n теряются. Попробую что-нибудь придумать.
Цикл ему кажется слишком длинным: на инструкции loop outer_loop выдает "Relative jump out of range by 22h bytes". И почему-то стал ругаться на невинную команду fstsw ax: "Illegal immediate".
P.S. Я решил оформить макросом приращение n и вычисление h, прилагаю код целиком.
.model small .stack 100h .data a dw 2 b dw 5 x dq 2.0 n dw 0 d dw 20 h dq ? y dq ?
.code
CalcF macro; На вершине стека д.б. параметр fld1 fld st(1) fyl2x fldln2 fmul fld1 fdivr
endm
iCalcF macro param fld1 fild param
fyl2x fldln2 fmul fld1 fdivr
endm
calc_h macro mov cx, n add cx, d mov n, cx dec cx
; вычисляем шаг finit fild b fisub a fidiv n fst h ; Сохраняем шаг endm
main proc
mov ax, @data mov ds, ax ; начинаем цикл по увеличению N mov cx, 3 ; Будут рассчитаны значения при N = 20, 40, 60
outer_loop: push cx calc_h
iCalcF b ; 1 / ln b fstp y iCalcF a ; 1 / ln a fadd y ; Складываем fdiv x ; Делим сумму на 2 fstp y ; Сохр. начальное приближение
; Теперь внутренний цикл - вычисляем интеграл fld x ; X в стек сопроцессора inner_loop: fadd h ; X <- X + h CalcF fadd y fstp y ; Сохраняем измененное значение интеграла loop inner_loop
fld y fmul h call outfloat
pop cx loop outer_loop;
outfloat proc near ;вывод числа push ax push cx push dx
push bp mov bp, sp push 10 push 0 ; Проверяем число на знак, и если оно отрицательное, ftst fstsw ax sahf jnc @of1 ; то выводим минус mov ah, 02h mov dl, '-' int 21h ; и оставляем модуль числа. fchs
@of1: fld1 fld st(1) fprem fsub st(2), st fxch st(2)
xor cx, cx
@of2: fidiv word ptr [bp - 2] fxch st(1) fld st(1) fprem fsub st(2), st fimul word ptr [bp - 2] fistp word ptr [bp - 4] inc cx push word ptr [bp - 4] fxch st(1) ftst fstsw ax sahf jnz short @of2 ; Теперь выведем её. mov ah, 02h @of3: pop dx add dl, 30h int 21h ; И так, пока не выведем все цифры. loop @of3 ; Теперь за дробную часть, для начала ;проверив её существование. fstp st(0) fxch st(1) ftst fstsw ax sahf jz short @of5 ; Если она ненулевая, выведем точку mov ah, 02h mov dl, '.' int 21h ; и не более четырех цифр дробной части. mov cx, 4 ; Помножим дробную часть на десять, @of4: fimul word ptr [bp - 2] fxch st(1) fld st(1) ; отделим целую часть fprem ; оставим от произведения лишь дробную часть, fsub st(2), st fxch st(2) ; сохраним полученную цифру во временной ячейке fistp word ptr [bp - 4] ; и сразу выведем. mov ah, 02h mov dl, [bp - 4] add dl, 30h int 21h ; Теперь, если остаток дробной части ненулевой fxch st(1) ftst fstsw ax sahf ; и мы вывели менее четырех цифр, продолжим. loopnz @of4 ; число выведено. Осталось убрать мусор из стэка. @of5: fstp st(0) fstp st(0) ; Точнее, стэков.
pop dx pop cx pop ax ret outfloat endp exit: mov ax, 4c00h ; int 21h ; main endp
Цикл ему кажется слишком длинным: на инструкции loop outer_loop выдает "Relative jump out of range by 22h bytes". И почему-то стал ругаться на невинную команду fstsw ax: "Illegal immediate".
Ты б за структурой следил, и вторую процедуру записывал бы после того, как первая закончится - было бы меньше неожиданностей. Насчет цикла - не подтверждается, код прекрасно собирается (помнят еще руки-то Цикл рассчитан до байта, добавляем еще одну инструкцию - 2 лишних байта для LOOP-а). На скрине - результат компиляции и запуска вот такого кода:
.286p .model small .stack 100h .data a dw 2 b dw 5 x dq 2.0 n dw 0 d dw 20 h dq ? y dq ? NL db 0Dh, 0Ah, "$"
.code
CalcF macro ; На вершине стека должен быть параметр fld1 fld st(1) fyl2x fldln2 fmul fld1 fdivr
endm
iCalcF macro param fld1 fild param
fyl2x fldln2 fmul fld1 fdivr
endm
CalcH macro
mov cx, n add cx, d mov n, cx dec cx ; вычисляем шаг fild b fisub a fidiv n fst h ; Сохраняем шаг
endm
main proc
mov ax, @data mov ds, ax
; начинаем цикл по увеличению N mov cx, 3 ; Будут рассчитаны значения при N = 20, 40, 60 outer_loop: finit ; выносим инициализацию сюда push cx CalcH
iCalcF b ; 1 / ln b fstp y iCalcF a ; 1 / ln a fadd y ; Складываем fdiv x ; Делим сумму на 2 fstp y ; Сохраняем начальное приближение
; Теперь внутренний цикл - вычисляем интеграл fld x ; Будем держать X в стеке сопроцессора inner_loop: fadd h ; X <- X + h CalcF fadd y fstp y ; Сохраняем измененное значение интеграла loop inner_loop
fld y fmul h ; Домножаем на h - получаем искомое значение call outfloat
pop cx loop outer_loop;
exit: mov ax, 4c00h ; int 21h ; main endp
; Требуется директива .286C или выше. outfloat proc near push ax push cx push dx ; Формируем кадр стэка, чтобы хранить в нём десятку ; и ещё какую-нибудь цифру. push bp mov bp, sp push 10 push 0 ; Проверяем число на знак, и если оно отрицательное, ftst fstsw ax sahf jnc @of1 ; то выводим минус mov ah, 02h mov dl, '-' int 21h ; и оставляем модуль числа. fchs ; Пояснение далее пойдёт на примере. ; ST(0) ST(1) ST(2) ST(3) ... ; Отделим целую часть от дробной. ; 73.25 ... что-то не наше @of1: fld1 ; 1 73.25 ... fld st(1) ; 73.25 1 73.25 ... ; Остаток от деления на единицу даст дробную часть. fprem ; 0.25 1 73.25 ... ; Если вычесть её из исходного числа, получится целая часть. fsub st(2), st ; 0.25 1 73 ... fxch st(2) ; 73 1 0.25 ... ; Сначала поработаем с целой частью. Считать количество цифр будем в CX. xor cx, cx ; Поделим целую часть на десять, @of2: fidiv word ptr [bp - 2] ; 7.3 1 0.25 ... fxch st(1) ; 1 7.3 0.25 ... fld st(1) ; 7.3 1 7.3 0.25 ... ; отделим дробную часть - очередную справа цифру целой части исходного числа,- fprem ; 0.3 1 7.3 0.25 ... ; от чатсного оставим только целую часть fsub st(2), st ; 0.3 1 7 0.25 ... ; и сохраним цифру fimul word ptr [bp - 2] ; 3 1 7 0.25 ... fistp word ptr [bp - 4] ; 1 7 0.25 ... inc cx ; в стэке. push word ptr [bp - 4] fxch st(1) ; 7 1 0.25 ... ; Так будем повторять, пока от целой части не останется ноль. ftst fstsw ax sahf jnz short @of2 ; Теперь выведем её. mov ah, 02h @of3: pop dx ; Вытаскиваем очередную цифру, переводим её в символ и выводим. add dl, 30h int 21h ; И так, пока не выведем все цифры. loop @of3 ; 0 1 0.25 ... ; Итак, теперь возьмёмся за дробную часть, для начала проверив её существование. fstp st(0) ; 1 0.25 ... fxch st(1) ; 0.25 1 ... ftst fstsw ax sahf jz short @of5 ; Если она всё-таки ненулевая, выведем точку mov ah, 02h mov dl, '.' int 21h ; и не более шести цифр дробной части. mov cx, 6 ; Помножим дрообную часть на десять, @of4: fimul word ptr [bp - 2] ; 2.5 1 ... fxch st(1) ; 1 2.5 ... fld st(1) ; 2.5 1 2.5 ... ; отделим целую часть - очередную слева цифру дробной части исходного числа,- fprem ; 0.5 1 2.5 ... ; оставим от произведения лишь дробную часть, fsub st(2), st ; 0.5 1 2 ... fxch st(2) ; 2 1 0.5 ... ; сохраним полученную цифру во временной ячейке fistp word ptr [bp - 4] ; 1 0.5 ... ; и сразу выведем. mov ah, 02h mov dl, [bp - 4] add dl, 30h int 21h ; Теперь, если остаток дробной части ненулевой fxch st(1) ; 0.5 1 ... ftst fstsw ax sahf ; и мы вывели менее шести цифр, продолжим. loopnz @of4 ; 0 1 ... ; Итак, число выведено. Осталось убрать мусор из стэка. @of5: fstp st(0) ; 1 ... fstp st(0) ; ... ; Точнее, стэков. leave
Решительно не хватает знаний, чтобы успешно завершить программу. Я ее расширил, чтобы n возрастало до тех пор, пока не будет достигнута требуемая точность (0.0001). Вместо вывода значения функции при каждом из n (20, 40, 60 ....) прога выводит окончательное значение, причем не один раз((. Вывод n также не работает. Прилагаю.
.286p .model small .stack 100h .data mes db 'Operands not comparable', 13, 10, '$' a dw 2 b dw 5 x dq 2.0 n dw 0 d dw 20 eps dd 0.0001 h dq ? y dq ? z dq ? NL db 0Dh, 0Ah, "$"
.code
CalcF macro; На вершине стека д.б. параметр fld1 fld st(1) fyl2x fldln2 fmul fld1 fdivr
endm
iCalcF macro param fld1 fild param
fyl2x fldln2 fmul fld1 fdivr
endm
Сalc_h macro mov cx, n add cx, d mov n, cx dec cx
; вычисляем шаг finit fild b fisub a fidiv n fst h ; Сохраняем шаг endm
main proc
mov ax, @data mov ds, ax
outer_: Сalc_h iCalcF b ; 1 / ln b fstp y iCalcF a ; 1 / ln a fadd y ; Складываем fdiv x ; Делим сумму на 2 fstp y ; Сохр. начальное
приближение
; внутренний цикл - вычисляем
интеграл fld x ; X в стек сопроцессора inner_loop: fadd h ; X <- X + h CalcF fadd y fstp y ; Сохраняем измененное
значение интеграла loop inner_loop
fld y fmul h
call outfloat call OutInt ;выводим актуальное n finit
; далее повторяем с начала, но сохраняем в z для последующего сравнения с y
Сalc_h iCalcF b ; 1 / ln b fstp z iCalcF a ; 1 / ln a fadd z ; Складываем fdiv x ; Делим сумму на 2 fstp z ; Сохр. начальное приближение fld x ;
lesser_loop: fadd h ; X <- X + h CalcF fadd z fstp z ; Сохраняем измененное значение интеграла loop lesser_loop
fld z fmul h
call outfloat
call OutInt ;выводим актуальное n
fld z fsub y fabs fcomp eps fstsw ax sahf
jp m2 jz m1 jc exit jmp outer_
m1: jmp outer_
m2: MOV DX, offset mes mov Ah,09 int 21h
exit: mov ax, 4c00h ; int 21h ; main endp
OutInt proc near xor ax,ax mov ax,n xor cx, cx mov bx, 10 l1: xor dx,dx div bx push dx inc cx test ax, ax jnz l1 mov ah, 02h l2: pop dx loop l2
mov AH,9 mov dx, offset NL int 21h ret outint endp
outfloat proc near ;вывод числа push ax push cx push dx
push bp mov bp, sp push 10 push 0 ; Проверяем число на знак, и если оно
отрицательное, ftst fstsw ax sahf jnc @of1 ; то выводим минус mov ah, 02h mov dl, '-' int 21h ; и оставляем модуль числа. fchs
@of1: fld1 fld st(1) fprem fsub st(2), st fxch st(2)
xor cx, cx
@of2: fidiv word ptr [bp - 2] fxch st(1) fld st(1) fprem fsub st(2), st fimul word ptr [bp - 2] fistp word ptr [bp - 4] inc cx push word ptr [bp - 4] fxch st(1) ftst fstsw ax sahf jnz short @of2 ; Теперь выведем её. mov ah, 02h @of3: pop dx add dl, 30h int 21h ; И так, пока не выведем все цифры. loop @of3 ; Теперь за дробную часть, для начала
;проверив её существование. fstp st(0) fxch st(1) ftst fstsw ax sahf jz short @of5 ; Если она ненулевая, выведем точку mov ah, 02h mov dl, '.' int 21h ; и не более четырех цифр дробной части. mov cx, 4 ; Помножим дробную часть на десять, @of4: fimul word ptr [bp - 2] fxch st(1) fld st(1) ; отделим целую часть fprem ; оставим от произведения лишь дробную
часть, fsub st(2), st fxch st(2) ; сохраним полученную цифру во временной
ячейке fistp word ptr [bp - 4] ; и сразу выведем. mov ah, 02h mov dl, [bp - 4] add dl, 30h int 21h ; Теперь, если остаток дробной части
ненулевой fxch st(1) ftst fstsw ax sahf ; и мы вывели менее четырех цифр, продолжим. loopnz @of4 ; число выведено. Осталось убрать мусор из
Hey, я опять не понял, зачем тебе 2 цикла, в каждом из которых ты вычисляешь значения интегралов? Давай запишем алгоритм который тебе нужен, в псевдокоде. Итак, чтобы определить, при каком N значение интеграла найдено с точностью Eps, тебе достаточно:
n <- 0 y <- 0.0; repeat prev <- y; n <- n + DeltaN; y <- Integral(a, b, n); until abs(y - z) < eps; print "finally Int = ", y, " when n = ", n
Всё, никаких двойных циклов, все прекрасно делается одним. А теперь смотри, как это записывается на ассемблере (я заменил переменную z на prev):
main proc mov ax, @data mov ds, ax
outer_: fld y fstp prev ; fldz ; fstp y
; вычисляем шаг для текущего N Сalc_h
iCalcF b ; 1 / ln b fstp y iCalcF a ; 1 / ln a fadd y ; Складываем fdiv x ; Делим сумму на 2 fstp y ; Сохр. начальное приближение
; внутренний цикл - вычисляем интеграл fld x ; X в стек сопроцессора inner_loop: fadd h ; X <- X + h CalcF ; F(X) fadd y fstp y ; Сохраняем измененное значение интеграла loop inner_loop
fld y fmul h ; нашли окончательное значение при текущем N fst y ; сохранили его назад в Y для последующего сравнения с prev
; дублируем значение с вершины стека ; fld st
; выводим значение интеграла и N call outfloat call OutInt ; выводим актуальное n
fld y fsub prev fabs fcomp eps fstsw ax sahf jp uncomp jz m1 jc exit m1: jmp outer_ uncomp: mov dx, offset mes mov ah, 09 int 21h
exit: mov ax, 4c00h ; int 21h ; main endp
Проще, правда, чем делать дважды одно и то же?
Теперь насчет
Цитата
Вывод n также не работает.
Процедуру OutInt проверять не пробовал?
Вообще-то, она должна выглядеть так (вообще-то, жестко задавать в программе, что процедура выводит именно N - это бред, лучше перед ее вызовом занести ax <- N):
outint proc near push cx push dx push bx push ax
mov ax, n ; <--- !!! А этого лучше не делать !!!
; Проверяем число на знак. test ax, ax jns short @oi1 ; Если оно отрицательное, выведем минус и оставим его модуль. mov ah, 02h mov dl, '-' int 21h pop ax push ax neg ax ; Количество цифр будем держать в CX. @oi1: xor cx, cx mov bx, 10 @oi2: xor dx, dx div bx ; Делим число на десять. В остатке получается последняя цифра. ; Сразу выводить её нельзя, поэтому сохраним её в стэке. push dx inc cx ; А с частным повторяем то же самое, отделяя от него очередную ; цифру справа, пока не останется ноль, что значит, что дальше ; слева только нули. test ax, ax jnz short @oi2 ; Теперь приступим к выводу. mov ah, 02h @oi3: pop dx ; Извлекаем очередную цифру, переводим её в символ и выводим. add dl, 30h int 21h ; Повторим ровно столько раз, сколько цифр насчитали. loop @oi3
; переводим строку после выведенного числа mov ah, 9 mov dx, offset NL int 21h
pop ax pop bx pop dx pop cx ret outint endp
С этими изменениями программа прекрасно отрабатывает:
IUnknown, ты прав: уперся я в свое решение как лунатик и увидеть больше ничего не мог). С одним циклом все внятно. Спасибо за терпение. Еще вопрос: в какой программе ты набираешь листинг? Явно не блокнот.