На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
! Перед отправкой сообщения внимательно прочтите правила раздела!!!
1. Запрещается обсуждать написание вирусов, троянов и других вредоносных программ!
2. Помните, что у нас есть FAQ раздела Assembler и Полезные ссылки. Не поленитесь, загляните в эти разделы перед созданием темы. Возможно, так уже имеется решение вашего вопроса.

3. Настоятельно рекомендуем обратить особое внимание на правила форума, которые нарушаются чаще всего:
  3.1. Заголовок темы должен кратко отражать её суть. Темы с заголовками типа "Срочно помогите!" или "Ассемблер" будут отправляться в Корзину для мусора.
  3.2. Исходники программ обязательно выделяйте тегами [code]...[/code] (одиночные инструкции можно не выделять).

Не забывайте также про главные Правила форума!

Так что добро пожаловать и приятного вам общения!!! ;)
 
Модераторы: Jin X, Qraizer
  
> сопроцессор, вещественная арифметика
Помогите разобратся:
ExpandedWrap disabled
    ...
    ;набрали 1-е число:  GetWindowTextA@12 --> eax
    ...
     plus:                ;нажали "+" (id_But equ 1Ch)
       mov   edx,0        ;очищаем регистр
       mov   edx,1Ch      ;отмечаем нажатую кнопку  
       mov   str1,eax     ;сохраняем что набрали
       finit              ;инициируем сопроцессор
       fldz               ;очищаем вершину стека 0-->st(0)
       fld   str1         ;добавляем первое число str1-->st(0)
       call  clear        ;очищаем дисплей  
    ...
    ;набрали 2-е число:  GetWindowTextA@12 --> eax
    ...
     itog:                     ;нажали "="
       cmp edx,1Ch                            
       je  plus_ex
    ...
    plus_ex:
       call  clear             ;очищаем дисплей
       mov   buf,0             ;очищаем буфер ?
       mov   str2,eax          ;сохраняем что набрали
       finit                   ;инициируем сопроцессор
       fadd  str2              ;st(0)+str2-->st(0)
       fst   buf               ;st(0)-->buf - здесь сумма
       push  offset buf        
       push  [hWndEdt]
       call  SetWindowTextA@8  ;выводим сумму на дисплей
       jmp   exit              ;mov eax,0  jmp finish
    ...
    finish:
      pop  edi
      pop  esi
      pop  ebx
      pop  ebp
      ret  16
    winproc endp
    clear  proc
      push  Edt
      push  [hWndEdt]
      call  SetWindowTextA@8
      ret
    clear  endp
    end go


В результате сложения (после нажатия "=") - на дисплее последнее набранное число. Такое впечатление, что первое число в st(0) не попало (fld str1) или, почему-то, от туда исчезло. Даже без конвертации что-то должно быть.
Сообщение отредактировано: Qraizer -
Как бы негоже проводить инициализацию на каждый раз. Так, в Винде на 32-битах у меня вообще пишет недопустимую операцию при добавлении числа к пустой ячейке. В 16-битном режиме что-то складывает с давно там записанным. :yes-sad:
finit уничтожает все данные во всех (в т.ч. специальных) регистрах сопроцессора. К незанятому регистру FPU ничего нельзя прибавить. В FPU есть специальные регистры-теги, которые хранят доп.информацию об основных стековых регистрах. Если регистр помечен как незанятый, то и результат будет "никаким".

Цитата cupoma58 @
Даже без конвертации что-то должно быть.
Если вы пытаетесь записать в st(0) строку, то там явно будет не то, что нужно. И это "что-то" может быть, например, NaN (нечисло) или Inf (бесконечность).

p.s. jmp exit или finish?
user posted image Чат форума в Telegramuser posted image Чат форума в Discord (жми и подключайся!) ;)
Цитата Jin X @
finit уничтожает все данные во всех (в т.ч. специальных) регистрах сопроцессора.
У меня книга "Assembler для Dos, Windows и Unix" (Москва, 1999) пишет:
Цитата
FINIT: ... Регистры данных никак не изменяются, но все они помечаются пустыми в регистре TW.
Собственно, 16-битный запуск подтвердил, что если после finit'а идёт сразу fadd, то происходит прибавление чего-то с чем-то. А вот Винда 32-битная пишет "1#IND" в регистре. Так что не всё столь однозначно. :)
Так и есть. Содержимое регистров помечается пустым. Это можно изменить в регистре тэгов, чем и "воскресить" хранящиеся там значения, однако смысла в этом нет никакого. FINIT предназначена для начальной инициализации, чтобы сбросить весь предыдущий неизвестный контекст, так что если контекст нужен, проще просто не выполнять инициализацию. У ТС второй FINIT абсолютно лишний.
При попытке выполнить какою-либо операцию, которая зависит от содержимого регистра, над пустым регистром ведёт к исключению Invalid Operation. После FINIT – да и вообще так в WinAPI принято в качестве дефолтного поведения – все исключения замаскированы, а маскированная реакция на пустой регистр – помещение в него вещественной неопределённости (разновидность NaN). Соответственно любая операция с NaN тоже порождает NaN.
Одни с годами умнеют, другие становятся старше.
Цитата Славян @
Как бы негоже проводить инициализацию на каждый раз. Так, в Винде на 32-битах у меня вообще пишет недопустимую операцию при добавлении числа к пустой ячейке. В 16-битном режиме что-то складывает с давно там записанным. :yes-sad:

Команда "finit" добавляется в обработку каждой кнопки операции, поскольку неизвестно - с какой операции начнётся работа арифмометра. Я так понимаю - из
"plus_ex" его лучше убрать ?

Добавлено
Цитата Qraizer @
Так и есть. Содержимое регистров помечается пустым. Это можно изменить в регистре тэгов, чем и "воскресить" хранящиеся там значения, однако смысла в этом нет никакого. FINIT предназначена для начальной инициализации, чтобы сбросить весь предыдущий неизвестный контекст, так что если контекст нужен, проще просто не выполнять инициализацию. У ТС второй FINIT абсолютно лишний.
При попытке выполнить какою-либо операцию, которая зависит от содержимого регистра, над пустым регистром ведёт к исключению Invalid Operation. После FINIT – да и вообще так в WinAPI принято в качестве дефолтного поведения – все исключения замаскированы, а маскированная реакция на пустой регистр – помещение в него вещественной неопределённости (разновидность NaN). Соответственно любая операция с NaN тоже порождает NaN.

Т.е. - вообще избавится от "finit" ?
Так. Смотри. FPU – это устройство, работающее по запросу. Т.е. вот есть команда, оно её выполняет, закончил и есть следующая, выполняет следующую, нет следующей если, останавливается и ждёт новых команд. Он работает параллельно с CPU, асинхронно, поэтому ситуация, когда у него кончились команды, а новых от CPU ещё не поступило – это нормально. При этом в состоянии останова он естественно сохраняет свой контекст неизменным, чтобы как только новая команда, тут же исполнить её на этом контексте. Это не CPU, который не умеет стоять и постоянно должен что-то делать. (На самом деле умеет, но для этого есть специальные команды. Т.е. фактически он даже когда стоит, то не стоит, а выполняет команду "стоять".) Поэтому, когда ты ему даёшь первую команду, FPU может хранить неизвестный тебе контекст от предыдущего потока исполнения. Для этого и нужна FINIT: полностью нейтрализовать влияние предыдущего контекста.
Но требуется это только в начале работы. Как только ты сделал FINIT, далее ты уже можешь управлять FPU единолично, т.к. он находится под контролем твоей программы. Т.к. ты можешь прогнозировать его состояние, сбрасывать контекст требуется очень редко. Можно сказать, что в подавляющем большинстве случаев и не требуется вовсе. (Если это не так, имеет смысл пересмотреть архитектуру приложения, чем заморачиваться многократными сбросами контекста по ходу её выполнения. Ну и есть ещё особые случаи, типа обработки исключительных ситуаций, чтобы сбросить состояние ошибки и вернуть FPU в нормальный режим.)
Увы, что-то конкретное посоветовать по твоему коду сложно. Непонятно даже, как ты получаешь числа из полей редактирования, ибо GetWindowText() возвращает в EAX длину текста в поле, переданного в приложение, а не введённое в него значение. И это не говоря уже о SetWindowText() которая работает с текстами, а не числами. По меньшей мере FLDZ не нужна, т.к. FLD и так загружает (теоретически) в вершину стека первый операнд, и не нужен второй FINIT, т.к. он полностью сбрасывает весь предыдущий контекст, и толку было ранее что-либо загружать.
Одни с годами умнеют, другие становятся старше.
Добавлю, что периодическое использование finit маскирует ошибки в программе, что не есть хорошо.
Любая нормальная функция должна "убирать за собой". Т.е. внесла 3 числа, 3 же числа с вершины стека и удалила (за исключением случая, когда нужно вернуть значение в регистре st0). Если где-то есть ошибка, finit её прикроет, но от этого она никуда не денется.
К тому же, функции могут вызываться друг за другом (хотя это не очень рекомендуется из-за небольшого размера стека FPU), когда результат предыдущей функции будет нужен далее. И finit удалит результат предыдущей функции.

Например, ln(x)+sqrt(y):
1. Загружаем x [st0=x]
2. Вызываем ln, получаем результат [st0=ln(x)]
3. Загружаем y [st0=y, st1=ln(x)]
4. Вызываем sqrt, получаем результат [st0=sqrt(y), st1=ln(x)] – если в sqrt будет finit, то st1 очистится (пометится как очищенный)
5. faddp – складываем x и y (если в sqrt был finit, то ничего не получится... и даже исключение будет замаскировано)
user posted image Чат форума в Telegramuser posted image Чат форума в Discord (жми и подключайся!) ;)
Цитата Qraizer @
Так. Смотри. FPU – это устройство, работающее по запросу. Т.е. вот есть команда, оно её выполняет, закончил и есть следующая, выполняет следующую, нет следующей если, останавливается и ждёт новых команд. Он работает параллельно с CPU, асинхронно, поэтому ситуация, когда у него кончились команды, а новых от CPU ещё не поступило – это нормально. При этом в состоянии останова он естественно сохраняет свой контекст неизменным, чтобы как только новая команда, тут же исполнить её на этом контексте. Это не CPU, который не умеет стоять и постоянно должен что-то делать. (На самом деле умеет, но для этого есть специальные команды. Т.е. фактически он даже когда стоит, то не стоит, а выполняет команду "стоять".) Поэтому, когда ты ему даёшь первую команду, FPU может хранить неизвестный тебе контекст от предыдущего потока исполнения. Для этого и нужна FINIT: полностью нейтрализовать влияние предыдущего контекста.
Но требуется это только в начале работы. Как только ты сделал FINIT, далее ты уже можешь управлять FPU единолично, т.к. он находится под контролем твоей программы. Т.к. ты можешь прогнозировать его состояние, сбрасывать контекст требуется очень редко. Можно сказать, что в подавляющем большинстве случаев и не требуется вовсе. (Если это не так, имеет смысл пересмотреть архитектуру приложения, чем заморачиваться многократными сбросами контекста по ходу её выполнения. Ну и есть ещё особые случаи, типа обработки исключительных ситуаций, чтобы сбросить состояние ошибки и вернуть FPU в нормальный режим.)
Увы, что-то конкретное посоветовать по твоему коду сложно. Непонятно даже, как ты получаешь числа из полей редактирования, ибо GetWindowText() возвращает в EAX длину текста в поле, переданного в приложение, а не введённое в него значение. И это не говоря уже о SetWindowText() которая работает с текстами, а не числами. По меньшей мере FLDZ не нужна, т.к. FLD и так загружает (теоретически) в вершину стека первый операнд, и не нужен второй FINIT, т.к. он полностью сбрасывает весь предыдущий контекст, и толку было ранее что-либо загружать.

Я скорректировал код в соответствии с рекомендациями:
ExpandedWrap disabled
      ...
    wmcommand:
      movzx eax,word ptr [ebp+10h]
      cmp   eax,..h            ;id_but --> "цифра или символ точка"                
      je    nabor
      cmp   eax,1Ch            ;id_but --> "+"                
      je    plus
      ...
      cmp   eax,24h            ;id_but --> "="                
      je    itog
      ...
      jmp   defwndproc
     
      ;обработка нажатия кнопок:
     
      nabor:
        ...
        call  SetWindowTextA@8  ;набрали 1-e число  ;в предыдущем варианте я ошибся с функцией
        ...
      plus:                     ;нажали "+"
        mov   edx,0             ;очищаем регистр и...
        mov   edx,hbutS         ;...сохраняем id кнопки --> "+"
        mov   str1,eax          ;сохраняем 1-е число
        finit                   ;инициируем сопроцессор
        fld   str1              ;str1-->st(0)
        call  clear             ;очищаем дисплей  
        ...
      ;набрали 2-е число...число набирается и остаётся на дисплее
        ...
      itog:                     ;нажали "="
        cmp   edx,hbutS         ;если нажали "+"...                
        je    plus_ex           ;...переходим сюда...не переходим
        ...
      plus_ex:
        call  clear             ;очищаем дисплей...очистка не срабатывает
        mov   str2,eax          ;сохраняем 2-e число в str2
        mov   eax,buf           ;освобождаем буфер  
        fadd  str2              ;st(0)+str2-->st(0)
        fst   buf               ;st(0)-->buf
        fwait
        push  buf               ;здесь сумма
        push  [hWndEdt]
        call  SetWindowTextA@8  ;выводим её на дисплей
        jmp   exit
        ...
      exit:
        mov  eax,0
        jmp  finish
        ...
      finish:
        pop  edi
        pop  esi
        pop  ebx
        pop  ebp
        ret  16
    winproc endp
    clear  proc
      push  Edt
      push  [hWndEdt]
      call  SetWindowTextA@8
      ret
    clear  endp
    end go

После нажатия "=" на дисплее остаётся 2-е набранное число. Убрал из "plus_ex:" всё, кроме "call clear" и "jmp exit" - картина не меняется. Похоже, что код в метке "itog:" не работает и переход к "plus_ex:" не происходит. В чём дело - понять не могу. Помогите разобратся.
--------------------
И ещё один момент: Цифры на дисплее набираются путём нажатия на кнопки арифмометра. В общем случае число - это "string". Число добавляется на дисплей при помощи SetWindowTextA, в результате в ЕАХ остаётся hex-вариант числа (?). И тогда конвертация идёт по цепочке hex-->bin-->hex, или, всё-таки,
str-->hex-->bin-->hex-->str ?
Цитата cupoma58 @
В чём дело - понять не могу. Помогите разобратся.
Ну, по приведённым фрагментам ничего сказать нельзя. Что такое hbutS, где лежит в EDX, каковы значения EAX и откуда ни берутся? Ты б полный текст привёл, что ли...

Добавлено
Второй вопрос тоже непонятен.
Цитата cupoma58 @
в результате в ЕАХ остаётся hex-вариант числа (?)
Если ты, у которого есть полный код, не знаешь, откуда мы-то можем знать?
Одни с годами умнеют, другие становятся старше.
1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
0 пользователей:


Рейтинг@Mail.ru
[ Script Execution time: 0,1212 ]   [ 19 queries used ]   [ Generated: 20.05.18, 11:47 GMT ]