Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

RFpro.ru: Ассемблер? Это просто! Учимся программировать


Хостинг портала RFpro.ru:
Московский хостер
Профессиональный ХОСТИНГ на базе Linux x64 и Windows x64

РАССЫЛКИ ПОРТАЛА RFPRO.RU

Лучшие эксперты по данной тематике

Коцюрбенко Алексей aka Жерар
Статус: Профессор
Рейтинг: 2739
∙ повысить рейтинг »
Boriss
Статус: Академик
Рейтинг: 2636
∙ повысить рейтинг »
Абаянцев Юрий Леонидович aka Ayl
Статус: Профессионал
Рейтинг: 2121
∙ повысить рейтинг »

/ КОМПЬЮТЕРЫ И СОФТ / Программирование / Assembler (Ассемблер)

Номер выпуска:1474
Дата выхода:21.09.2011, 11:30
Администратор рассылки:Лысков Игорь Витальевич (Старший модератор)
Подписчиков / экспертов:200 / 62
Вопросов / ответов:1 / 1

Консультация # 184029: Уважаемые эксперты! Пожалуйста, ответьте на вопрос: Надеюсь последняя моя проблема с Ассемблером. Данные задания уже были выполнены, как вставка в паскаль, но теперь те же программы должны быть реализована на языке ассемблера TASM. 1. Представить обыкновенную дробь как запись с полями «Числитель» и «Знаменатель» и реализовать арифм...


Консультация # 184029:

Уважаемые эксперты! Пожалуйста, ответьте на вопрос:
Надеюсь последняя моя проблема с Ассемблером.

Данные задания уже были выполнены, как вставка в паскаль, но теперь те же программы должны быть реализована на языке ассемблера TASM.

1. Представить обыкновенную дробь как запись с полями «Числитель» и «Знаменатель» и реализовать арифметические операции с дробями. (Под записью подразумевается структура с упакованными данными)

2. Реализуйте подпрограмму, которая возвращает символы имеющиеся в данной строке с указанием числа вхождений каждого символа.

3. Дано натуральное число n. Вычислить: (1-1/2!)(1-1/3!)(1-1/4!)…(1-1/n!) (Все повторяющиеся фрагменты должны быть выполнены в виде макросов, вычисления проводятся до пятого знака после запятой)

Ссылки на прошлые ответы: первая, вторая

Благодарю.

Дата отправки: 15.09.2011, 15:56
Вопрос задал: Посетитель - 380267 (Посетитель)
Всего ответов: 1
Страница онлайн-консультации »


Консультирует Лысков Игорь Витальевич (Старший модератор):

Здравствуйте, Посетитель - 380267!
1) Дроби

Сама программа 184029_1.asm

Код :
 .386
 .model flat,STDCALL

o   equ offset
d   equ dword ptr
STD_INPUT_HANDLE equ (-10)
STD_OUTPUT_HANDLE equ (-11)
NULL   equ 0

;дробь - запись из двух полей: числитель (x) и знаменатель(y)
fraction struct
x dd ?
y dd ?
fraction ends

extrn wsprintfA:PROC
extrn AllocConsole:PROC
extrn GetStdHandle:PROC
extrn lstrlen:PROC
extrn WriteConsoleA:PROC
extrn ReadConsoleA:PROC
extrn GetLastError:PROC
extrn ExitProcess:PROC

 .code

;ввод строки с консоли
;используется только для ожидания нажатия на Enter
StdIn proc lpszText:DWORD, dwLen:DWORD
LOCAL bRead :DWORD
 call GetStdHandle,STD_INPUT_HANDLE
 lea ecx, bRead
 call ReadConsoleA, eax, lpszText, dwLen, ecx, 0
 ret
StdIn endp

;вывод строки на консоль
StdOut proc lpszText:DWORD
LOCAL hOutPut :DWORD
LOCAL bWritten :DWORD

 call GetStdHandle,STD_OUTPUT_HANDLE
 mov hOutPut, eax

 call lstrlen, lpszText ;EAX = длина строки
 lea ecx, bWritten  ;ECX = адресу переменной, куда запишется длина
     ; реально выведенного сообщения
 call WriteConsoleA,hOutPut,lpszText,eax,ecx,NULL
 mov eax, bWritten  ;вернем длину
 ret
StdOut endp

;функция вычисления наибольшего общего делителя чисел A и B
;числа A в EAX и B в EDX, результат НОД в EAX
GCD proc
 push ecx  ;сохраним регистр ECX
 mov ecx,edx  ;число В в регистре EDX, при делении 
    ; будет портиться, пусть будет в ECX
 test eax,eax  ;возьмем модуль обоих чисел
 jns @test_neg_B
 neg eax
 test_neg_B:
 test ecx, ecx
 jns @GCD_continue
 neg ecx
 GCD_continue:   ;цикл расчета НОД-а
 test eax,eax  ;пока одно из чисел не станет равным 0
 jz @GCD_ret
 jcxz @GCD_ret
 cmp eax,ecx  ;будем искать остаток от деления большего на меньшее
 jb @GCD_A_lt_B ;A < B
                     ;A >= B
 xor edx,edx  ;числа положительные, можно смело обнулять
 div ecx  ;A / B
 mov eax,edx  ;EAX = A = A mod B, ECX = B
 jmp @GCD_continue
 GCD_A_lt_B:   ;A < B
 xchg eax,ecx  ;EAX = B, ECX = A
 xor edx,edx
 div ecx  ;B / A
 mov eax,edx  ;EAX = B mod A
 xchg eax,ecx  ;ECX = B mod A, EAX = A
 jmp @GCD_continue
 GCD_ret:
 add eax,ecx  ;EAX = A + B, или то, или другое, 
    ;т.к. второе будет равно 0}
 pop ecx
 ret
GCD endp

;процедура деления дроби на НОД
;EAX - НОД, EBX - знаменатель, EDI - числитель, ECX - адрес дроби-результата}

Cut_f proc
 XCHG EAX,EBX  ;EAX = знаменатель, EBX = НОД
 CDQ   ;знаковое расширение до DX:AX
 IDIV EBX  ;EAX = знаменатель / НОД
 XCHG EAX,EDI  ;EAX = числитель, EDI = знаменатель / НОД
 CDQ   ;знаковое расширение до DX:AX
 IDIV EBX  ;EAX = числитель / НОД
    ;пусть отрицательное число будет только в числителе
 TEST EDI,EDI  ;знак знаменателя
 jns @SET_NUMS
 NEG EAX  ;умножим на -1
 NEG EDI
 SET_NUMS:   ;сохраним результат
 MOV [ECX].x,EAX
 MOV [ECX].y,EDI
 ret
Cut_f endp

;процедура складывания двух дробей
;параметры: EAX и EDX - адреса складаемых, ECX - результата
;правило вычисления для x1/y1 + x2/y2
;(первая строка - числитель, вторая - знаменатель):
;(x1*y2+x2*y1) div GCD(x1*y2+x2*y1, y1*y2)
;(y1*y2) div GCD(x1*y2+x2*y1, y1*y2)
Add_f proc
 PUSH EBX  ;сохраним регистры
 PUSH ESI
 PUSH EDI

 MOV ESI,EAX  ;адрес первой дроби
 MOV EDI,EDX  ;второй

 MOV EAX,[ESI].y
 IMUL [EDI].y
 MOV EBX,EAX  ;EBX = y1*y2}

 MOV EAX,[ESI].x
 IMUL [EDI].y
 XCHG EAX,EDI  ;EDI = x1*y2, EAX = адресу второй дроби
 MOV EAX,[EAX].x
 MUL [ESI].y
 ADD EDI,EAX  ;EDI = x1*y2 + x2*y1

 MOV EAX,EDI  ;числитель
 MOV EDX,EBX  ;знаменатель
 call GCD  ;EAX = НОД

 call Cut_f  ;делим на НОД и сохраняем

 POP EDI
 POP ESI
 POP EBX
 ret
Add_f endp

;процедура вычитания двух дробей
;параметры: EAX - адрес уменьшаемого, EDX - вычитаемого и ECX - результата
;правило вычисления для x1/y1 - x2/y2
;(первая строка - числитель, вторая - знаменатель):
;(x1*y2-x2*y1) div GCD(x1*y2-x2*y1, y1*y2)
;(y1*y2) div GCD(x1*y2-x2*y1, y1*y2)
Sub_f proc
 PUSH EBX
 PUSH ESI
 PUSH EDI

 MOV ESI,EAX
 MOV EDI,EDX

 MOV EAX,[ESI].y
 IMUL [EDI].y
 MOV EBX,EAX

 MOV EAX,[ESI].x
 IMUL [EDI].y
 XCHG EAX,EDI
 MOV EAX,[EAX].x
 IMUL [ESI].y
 SUB EDI,EAX  ;аналогично сумме, отличие только здесь!

 MOV EAX,EDI
 MOV EDX,EBX
 call GCD

 call Cut_f

 POP EDI
 POP ESI
 POP EBX
 ret
Sub_f endp

;процедура умножения двух дробей
;параметры: EAX, EDX - адреса множителей и ECX - результата}
;правило вычисления для x1/y1 * x2/y2
;(первая строка - числитель, вторая - знаменатель):
;(x1*x2) div GCD(x1*x2, y1*y2)
;(y1*y2) div GCD(x1*x2, y1*y2)
Mul_f proc
 PUSH EBX
 PUSH ESI
 PUSH EDI

 MOV ESI,EAX
 MOV EDI,EDX

 MOV EAX,[ESI].y
 IMUL [EDI].y
 MOV EBX,EAX   ;EBX = y1*y2
 MOV EAX,[ESI].x
 IMUL [EDI].x
 MOV EDI,EAX   ;EAX = EDI = x1*x2

 MOV EDX, EBX  ;второй множитель, первый уже в EAX
 call GCD   ;НОД

 call Cut_f   ;делим на НОД и сохраняем

 POP EDI
 POP ESI
 POP EBX
 ret
Mul_f endp

;процедура деления двух дробей
;параметры: EAX - адрес делимого, EDX - делителя и ECX - результата
;правило вычисления для x1/y1 * x2/y2
;(первая строка - числитель, вторая - знаменатель):
;(x1*y2) div GCD(x1*y2, y1*x2)
;(y1*x2) div GCD(x1*y2, y1*x2)
Div_f proc
 PUSH EBX
 PUSH ESI
 PUSH EDI

 MOV ESI,EAX
 MOV EDI,EDX

 MOV EAX,[ESI].y
 IMUL [EDI].x
 MOV EBX,EAX   ;EBX = y1*x2
 MOV EAX,[ESI].x
 IMUL [EDI].y
 MOV EDI,EAX   ;EDI = x1*y2

 MOV EDX,EBX   ;делитель, делимое уже в EAX

 call GCD   ;НОД

 call Cut_f   ;делим на НОД и сохраняем

 POP EDI
 POP ESI
 POP EBX
 ret
Div_f endp

;вывод сообщения
;параметры: три дроби и знак операции
print proc fr1:dword,fr2:dword,fr3:dword,oper:dword
 mov eax,fr1
 mov edx,fr2
 mov ecx,fr3 
 call wsprintfA C,o buffer,o format,\
  d[eax].x,d[eax].y,oper,d[edx].x,d[edx].y,d[ecx].x,d[ecx].y

 call StdOut, o buffer
 ret
print endp

start: 
 call AllocConsole  ;создадим консоль
 test ax, ax
 jz exit   ;если ошибка, то просто выйдем

 mov f1.x,3   ;проинициируем исходные дроби 3/4 и -9/16
 mov f1.y,4
 mov f2.x,-9
 mov f2.y,16

;деление
 lea eax,f1   ;делимое
 lea edx,f2   ;делитель
 lea ecx,f3   ;частное
 call Div_f
 call print,o f1,o f2,o f3,'/';выведем результат

;умножение
 lea eax, f1
 lea edx, f2
 lea ecx, f3
 call Mul_f
 call print,o f1,o f2,o f3,'*'

;сумма
 lea eax, f1
 lea edx, f2
 lea ecx, f3
 call Add_f
 call print,o f1,o f2,o f3,'+'

;разность
 lea eax, f1
 lea edx, f2
 lea ecx, f3
 call Sub_f
 call print,o f1,o f2,o f3,'-'

;подождем ввода чего-либо или просто нажатия на Enter,
;чтобы не консоль не сразу закрылась
 call StdIn,o buffer,256
exit:
 call ExitProcess STDCALL, 0

 .data
;формат для вывода сообщения
format db '%d/%d %c %d/%d = %d/%d',0dh,0ah,0
;дроби
f1 fraction <>
f2 fraction <>
f3 fraction <>
;буфер
buffer db 256 dup (?)
 end start


Файл 184029_1.def, в котором, в частности, задаем импорт функции wsprintfA из User32.dll
Код :
NAME      184029_1

DESCRIPTION 'Assembly Win32 Program'

CODE      PRELOAD MOVEABLE DISCARDABLE
DATA      PRELOAD MOVEABLE MULTIPLE

EXETYPE      WINDOWS

HEAPSIZE     65536
STACKSIZE    65536

IMPORTS      USER32.wsprintfA



Для создания EXE удобно пользоваться утилитой MAKE.EXE, которая вызывает все нужные команды

Makefile.mak
Код :
#   Make file for Turbo Assembler 184029_1.

# make -B   Will build the release version of 184029_1.exe
# make -B -DDEBUG  Will build the debug version of 184029_1.exe


MAKEDIR = c:\TASM\BIN
NAME = 184029_1
OBJS = $(NAME).obj
DEF = $(NAME).def

!if $d(DEBUG)
TASMDEBUG=/zi
LINKDEBUG=/v
!else
TASMDEBUG=
LINKDEBUG=
!endif

!if $d(MAKEDIR)
IMPORT=$(MAKEDIR)\..\lib\import32
THEINCLUDE=/i$(MAKEDIR)\..\include
!else
IMPORT=..\..\..\lib\import32
THEINCLUDE=
!endif

$(NAME).EXE: $(OBJS) $(DEF)
  $(MAKEDIR)\tlink32 /c /m /s /M /aa /Tpe $(LINKDEBUG) $(OBJS),$(NAME),$(NAME),$(IMPORT),$(DEF)

asm.obj:
  $(MAKEDIR)\tasm32 $(TASMDEBUG) /ml $(THEINCLUDE) $&.asm,,$&


Ну и, наконец, вызываем этот самый MAKE.EXE с помощью вот такого BAT-файла:
Код :
@c:\TASM\BIN\make -b %1

----------------------------------------------------------------------------------------------------------
2) Строки
Программа работает со строками формата языка С, т.е. заканчивающимися нулем
Программа предполагает, что длина строки не больше 254 байт,
поэтому и счетчики разных букв состоят из одного байта

184029_2.asm
Код :
 .386
 .model flat,STDCALL

o   equ offset
d   equ dword ptr
STD_INPUT_HANDLE equ (-10)
STD_OUTPUT_HANDLE equ (-11)
NULL   equ 0

extrn wsprintfA:PROC
extrn AllocConsole:PROC
extrn GetStdHandle:PROC
extrn lstrlen:PROC
extrn WriteConsoleA:PROC
extrn ReadConsoleA:PROC
extrn ExitProcess:PROC

 .code

;ввод строки с консоли
StdIn proc lpszText:DWORD, dwLen:DWORD
LOCAL bRead :DWORD
 call GetStdHandle,STD_INPUT_HANDLE
 lea ecx, bRead
 call ReadConsoleA, eax, lpszText, dwLen, ecx, 0
 ret
StdIn endp

;вывод строки на консоль
StdOut proc lpszText:DWORD
LOCAL hOutPut :DWORD
LOCAL bWritten :DWORD

 call GetStdHandle,STD_OUTPUT_HANDLE
 mov hOutPut, eax

 call lstrlen, lpszText ;EAX = длина строки
 lea ecx, bWritten  ;ECX = адресу переменной, куда запишется длина
     ; реально выведенного сообщения
 call WriteConsoleA,hOutPut,lpszText,eax,ecx,NULL
 mov eax, bWritten  ;вернем длину
 ret
StdOut endp

;процедура формирует строку из разных символов и массив из количеств символов
CharsCount proc Src:dword,Dst:dword,Counts:dword
 push edi  ;сохраним регистры
 push esi
 push ebx
 mov edx,Dst  ;адрес буфера разных символов
 mov esi,Src  ;адрес исходной строки
 mov edi,Counts ;адрес счетчиков
 mov ecx,256  ;предполагаем,что там 256 байт
 xor eax,eax  ;0
 rep stosb  ;обнуляем
 mov edi,Counts ;адрес счетчиков
 CalcCharsLoop:   ;цикл по символам строки
 lodsb   ;очередной символ
 cmp al,0dh
 je @FormString
 cmp al,0
 je @FormString
 inc byte ptr [edi+eax] ;считаем в массиве счетчиков
 loop @CalcCharsLoop  ;по всем символам
 FormString:   ;формируем результат
 xor ebx,ebx  ;индекс для 256 значений счетчиков
 xor esi,esi  ;счетчик разных значений
 mov ecx,256  ;счетчик в буфере
 FormStringLoop:  ;цикл формирования результата
 mov al,[edi+ebx] ;читаем счетчик
 test   al,al  ;проверяем на 0
 jz  @FormStringNext;если 0,то обходим
 mov [edx+esi],bl ;для ненулевого сохраняем индекс,как код символа
 mov [edi+esi],al ;и количество,сохраняем с начала массива счетчиков
 inc esi  ;считаем разные значения
 FormStringNext:  ;на следующий счетчик
 inc ebx  ;индекс следующего счетчика
 loop  @FormStringLoop ;по всем
 mov eax,esi  ;вернем длину строки разных символов
 mov byte ptr [edx+esi],0;закроем строку нулем
 pop ebx  ;восстанавливаем регистры
 pop esi
 pop edi
 ret
CharsCount endp

start: 
 call AllocConsole  ;создадим консоль
 test ax,ax
 jz exit   ;если ошибка,то просто выйдем

 call StdOut,o sEnter  ;пригласим ввести строку
 call StdIn,o buffer,256 ;вводим строку длины максимум 256, 
     ; в конце коды 0dh,0ah
;обработаем строку
 call CharsCount,o buffer,o Tgt,o Cnt

;сформируем результат
 call StdOut,o sTarget ;выведем 'Target ='
 xor esi,esi   ;индекс в массиве разных символов и количеств
PrCountsLoop:    ;по разнам символам
 mov al,Tgt[esi]  ;очередной символ
 cmp al,0   ;конец строки?
 je finish
 movsx ecx,Cnt[esi]  ;считываем количество, 1 байт в dword
 inc esi   ;индекс следующего
;сформирум информацию о символе в виде "'sym':N," (символ 'sym' N раз)
 call wsprintfA,o buffer,o frm_cd,eax,ecx
 cmp byte ptr Tgt[esi],0 ;если последний
 jne PrNum
 call lstrlen,o buffer ;то последнюю запятую
 mov dword ptr buffer[eax-1],0a0dh ;меняем на 0d0ah с нулем в конце
PrNum:
 call StdOut,o buffer  ;выводим
 jmp PrCountsLoop  ;на следующий
finish:
;подождем ввода чего-либо или просто нажатия на Enter,
;чтобы консоль не сразу закрылась
 call StdIn,o buffer,256
exit:
 call ExitProcess STDCALL,0

 .data
sEnter db 'Enter string: ',0
sTarget db 'Target: ',0
sCounts db 0dh,0ah,'Counts = ',0
frm_cd db "'%c':%d,",0
;буфер
buffer db 256 dup (?)  ;буфер для ввода, для формирования строк
Tgt db 256 dup (?)  ;буфер для строки разных символов
Cnt db 256 dup (?)  ;буфер для количеств разных символов

 end start


184029_2.def аналогичен
Код :
NAME      184029_2

DESCRIPTION 'Assembly Win32 Program'

CODE      PRELOAD MOVEABLE DISCARDABLE
DATA      PRELOAD MOVEABLE MULTIPLE

EXETYPE      WINDOWS

HEAPSIZE     65536
STACKSIZE    65536

IMPORTS      USER32.wsprintfA


Как и makefile.mak
Код :
#   Make file for Turbo Assembler 184029_2.

# make -B   Will build the release version of 184029_2.exe
# make -B -DDEBUG  Will build the debug version of 184029_2.exe


MAKEDIR = c:\TASM\BIN
NAME = 184029_2
OBJS = $(NAME).obj
DEF = $(NAME).def

!if $d(DEBUG)
TASMDEBUG=/zi
LINKDEBUG=/v
!else
TASMDEBUG=
LINKDEBUG=
!endif

!if $d(MAKEDIR)
IMPORT=$(MAKEDIR)\..\lib\import32
THEINCLUDE=/i$(MAKEDIR)\..\include
!else
IMPORT=..\..\..\lib\import32
THEINCLUDE=
!endif

$(NAME).EXE: $(OBJS) $(DEF)
  $(MAKEDIR)\tlink32 /c /m /s /M /aa /Tpe $(LINKDEBUG) $(OBJS),$(NAME),$(NAME),$(IMPORT),$(DEF)

asm.obj:
  $(MAKEDIR)\tasm32 $(TASMDEBUG) /ml $(THEINCLUDE) $&.asm,,$&


184029_2.bat так, вообще, один в один
Код :
@c:\TASM\BIN\make -b %1

----------------------------------------------------------------------------------------------------------
3) с факториалом
184029_3.asm
Код :
 .386
 .model flat,STDCALL

o   equ offset
d   equ dword ptr
STD_INPUT_HANDLE equ (-10)
STD_OUTPUT_HANDLE equ (-11)
NULL   equ 0

extrn wsprintfA:PROC
extrn AllocConsole:PROC
extrn GetStdHandle:PROC
extrn lstrlen:PROC
extrn WriteConsoleA:PROC
extrn ReadConsoleA:PROC
extrn ExitProcess:PROC

 .code

;ввод строки с консоли
StdIn proc lpszText:DWORD, dwLen:DWORD
LOCAL bRead :DWORD
 call GetStdHandle,STD_INPUT_HANDLE
 lea ecx, bRead
 call ReadConsoleA, eax, lpszText, dwLen, ecx, 0
 ret
StdIn endp

;вывод строки на консоль
StdOut proc lpszText:DWORD
LOCAL hOutPut :DWORD
LOCAL bWritten :DWORD

 call GetStdHandle,STD_OUTPUT_HANDLE
 mov hOutPut, eax

 call lstrlen, lpszText ;EAX = длина строки
 lea ecx, bWritten  ;ECX = адресу переменной, куда запишется длина
     ; реально выведенного сообщения
 call WriteConsoleA,hOutPut,lpszText,eax,ecx,NULL
 mov eax, bWritten  ;вернем длину
 ret
StdOut endp

;процедура вычисляет требуемое выражение, EAX = n
;результат - в стеке сопроцессора
CalcValue proc
 sub esp,4   ;здесь будет число 2...n
 mov dword ptr [esp],2 ;начинаем с 2
 finit    ;инициализация сопроцессора
 fld1    ;1, для накопления окончательного выражения
 fld st   ;1, для накопления факториала
 mainLoop:    ;цикл расчета
 cmp [esp],eax  ;проверим, дошли ли до конца, EAX = n
 ja @finish   ;конец расчета
 fimul dword ptr [esp]  ;считаем факториал, умножая предыдущее значение на очередное
 fld st   ;посчитаем числитель, как i!-1
 fld1    ;1
 fsubp st(1),st  ;числитель
 fdiv st, st(1)  ;получим (i!-1)/i!
 fmulp st(2),st  ;накапливаем произведения с потерей промежуточного частного
 inc dword ptr [esp]  ;на следующий член
 jmp @mainLoop  ;пока не дойдем до n
 finish:    ;конец
 fstp st   ;уберем из стека сопроцессора n!, теперь st = посчитанной величине!
 add esp,4   ;уберем двойное слово из стека процессора
 ret
CalcValue endp

;Функция преобразования строки в число
;признаками конца являются ноль, код 0dh, а также пробел и табуляция
;все остальные, кроме цифр, недопустимы
;также ошибкой будет число большее максимального целого (2^32-1)
;параметр - адрес строки, результат в EAX
atoi proc string:dword
 xor edi,edi   ;наше число
 mov esi,string  ;числовая строка
GetNumLoop:
 movzx ebx,byte ptr [esi] ;очередная цифра
 inc esi
; проверим на разделители
 cmp bl, 0dh
 je num_end_found  ;конец ввода
 cmp bl, 0
 je num_end_found
 cmp bl, ' '
 je num_end_found
 cmp bl, 9
 je num_end_found
 cmp bl, '0'   ;цифра?
 jb set_c   ;ошибка - не цифра!
 cmp bl, '9'
 ja set_c

 and bl,0fh   ;цифра -> число (30h-39h -> 0-9)
 mov eax,10   ;умножим на 10 
 imul edi   ;предыдущее значение
 test edx,edx   ;больше cлова - ошибка!
 jnz set_c
 add eax,ebx   ;добавим сохраненный разряд
 jc set_c   ;больше  слова - ошибка!
 js set_c   ;больше 2^31-1 - ошибка!
 mov edi,eax   ;сохраним
 jmp GetNumLoop  ;на анализ следующего символа

num_end_found:    ;встретили признак конца
 mov eax,edi   ;результат в eax
 clc    ;все ок
 ret
set_c:
 stc    ;ошибка
 ret
atoi endp

start: 
 call AllocConsole  ;создадим консоль
 test ax,ax
 jz exit   ;если ошибка,то просто выйдем

Continue:
 call StdOut,o sEnter  ;пригласим ввести N
 call StdIn,o buffer,256 ;вводим строку длины максимум 256, 
     ; в конце коды 0dh,0ah
 call atoi,o buffer  ;EAX = введенному числу
 jnc ToCmpValue  ;нет ошибки - идем дальше
 call StdOut,o sErrorNum ;вывод сообщение
 jmp Continue  ;и на повтор ввода
ToCmpValue:    ;проверим на значение
 cmp eax,2
 jl ToErrorValue
 cmp eax,1000
 jle ToCalc
ToErrorValue:
 call StdOut,o sErrorValue ;ждем число от 1 до 1000
 jmp Continue
ToCalc:
 call CalcValue  ;выполняем задание, EAX = n
     ;чтобы вывести вещественное число
     ;сделаем такой трюк
 fimul c100000   ;умножаем на 100000, чтобы 5 цифр после запятой
     ;оказались в целой части
 fistp dqValue   ;сохраним из сопроцессора, как 64-битное целое
 mov eax, dword ptr dqValue ;dx:ax = целому
 mov edx, dword ptr dqValue+4
 div c100000   ;делим обратно на 100000
     ;EAX = целой части, EDX = дробной
     ;сформируем строку в виде <целая часть>.<дробная>
     ;(оказалось, что у wsprintf нет формата для вывода
     ;вещественного числа)
 call wsprintfA,o buffer,o frm_value,eax,edx
 call StdOut,o buffer  ;выводим

;подождем ввода чего-либо или просто нажатия на Enter,
;чтобы консоль не сразу закрылась
 call StdIn,o buffer,256
exit:
 call ExitProcess STDCALL,0

 .data
dqValue  dq ?
c100000  dd 100000
sEnter  db 'Enter N: ',0
sErrorNum db 'Incorrect number',0dh,0ah,0
sErrorValue db 'Number must be from 2 to 1000',0dh,0ah,0
frm_value db 'Value = %d.%5.5d',0dh,0ah,0
;буфер
buffer db 256 dup (?)  ;буфер для ввода, для формирования строк

 end start

184029_3.def
Код :
NAME      184029_3

DESCRIPTION 'Assembly Win32 Program'

CODE      PRELOAD MOVEABLE DISCARDABLE
DATA      PRELOAD MOVEABLE MULTIPLE

EXETYPE      WINDOWS

HEAPSIZE     65536
STACKSIZE    65536

IMPORTS      USER32.wsprintfA


makefile.mak
Код :
#   Make file for Turbo Assembler 184029_3.

# make -B   Will build the release version of 184029_3.exe
# make -B -DDEBUG  Will build the debug version of 184029_3.exe

MAKEDIR = c:\TASM\BIN
NAME = 184029_3
OBJS = $(NAME).obj
DEF = $(NAME).def

!if $d(DEBUG)
TASMDEBUG=/zi
LINKDEBUG=/v
!else
TASMDEBUG=
LINKDEBUG=
!endif

!if $d(MAKEDIR)
IMPORT=$(MAKEDIR)\..\lib\import32
THEINCLUDE=/i$(MAKEDIR)\..\include
!else
IMPORT=..\..\..\lib\import32
THEINCLUDE=
!endif

$(NAME).EXE: $(OBJS) $(DEF)
  $(MAKEDIR)\tlink32 /c /m /s /M /aa /Tpe $(LINKDEBUG) $(OBJS),$(NAME),$(NAME),$(IMPORT),$(DEF)

asm.obj:
  $(MAKEDIR)\tasm32 $(TASMDEBUG) /ml $(THEINCLUDE) $&.asm,,$&

184029_3.bat
Код :
@c:\liv\temp\SYS\LIV\TASM\BIN\make -b %1

Консультировал: Лысков Игорь Витальевич (Старший модератор)
Дата отправки: 16.09.2011, 01:50
Рейтинг ответа:

НЕ одобряю 0 одобряю!


Оценить выпуск | Задать вопрос экспертам

главная страница  |  стать участником  |  получить консультацию
техническая поддержка  |  восстановить логин/пароль

Дорогой читатель!
Команда портала RFPRO.RU благодарит Вас за то, что Вы пользуетесь нашими услугами. Вы только что прочли очередной выпуск рассылки. Мы старались. Пожалуйста, оцените его. Если совет помог Вам, если Вам понравился ответ, Вы можете поблагодарить автора - для этого в каждом ответе есть специальные ссылки. Вы можете оставить отзыв о работе портале. Нам очень важно знать Ваше мнение. Вы можете поближе познакомиться с жизнью портала, посетив наш форум, почитав журнал, который издают наши эксперты. Если у Вас есть желание помочь людям, поделиться своими знаниями, Вы можете зарегистрироваться экспертом. Заходите - у нас интересно!
МЫ РАБОТАЕМ ДЛЯ ВАС!



В избранное