Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Низкоуровневое программирование > Assembler - Ассемблер (FASM, MASM, WASM, NASM, GoASM, Gas, RosAsm, HLA) и не рекомендуем TASM
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 10.04.2012, 14:31   #1
Glaciuse
Пользователь
 
Регистрация: 22.05.2011
Сообщений: 44
По умолчанию Программа для 8086 ругается на недопустимую инструкцию.

Пишу простенькую программу вычисления выражения (A*B-C)/D
Все работает, пока не вызываю процедуру конвертации из int в str

Процедура:

Код:
convertIntToStr proc near ;Процедура преобразования int в ASCII, преобразуемое число в AX, знак числа в CH

	mov buf[1], 00h ;Устанавилваем кол-во элементов в буфере
	mov bp, 02h 	;Указатель на 1й элемент массива
	mov bx, 0Ah		;Записываем в AX основание системы счисления, на которую будем делить
	cmp ax, 00h		;Если в AX - 0, то прыгаем на emptyAx
	je emptyAx
	
	cmp ch, 01h		;Проверяем что за знак в ch (0 - '+', 1 - '-')
	jne pushing		;Прыгаем, если в ch = 0
	neg ax			;Если в ch была 1, то инвертируем AX
	add buf[1], 01h	;Увеличиаем кол-во лементов в буфере на 1 , т.к. будет знак '-'
	mov buf[2], 2Dh	;Помещаем в 1 ячейку буфера знак - 
	add bp, 01h		;Увеличиваем указатель на 1й элемент массива

pushing:
	xor dx, dx		;Очищаем регистр DX, т.к. там может находиться остаток от деления
	div bx			;Делим AX на BX
	push dx			;Записываем остаток в стек
	inc cl			;Увеличиваем кол-во записыванных символов в стек
	
	cmp ax, 00h 	;Сравниваем АХ с 0
	je poping		;Если он равен 0, то переходим (деление закончено)
	jmp pushing		;Иначе продожаем деление

poping:
	pop dx			;Вытаскиваем верхний символ из стека
	add dx, 30h		;Добавляем 30,ч тобы получить ASCII код
	add buf[1], 01h	;Увеличиваем кол-во элементов в буфере на 1
	mov buf[bp], dl	;Помещаем ASCII символ в буфер
	inc bp			;Увеличиваем указатель на след элемент буфера
	dec cl			;Уменьшаем счетчик кол-ва элекментов в стеке
	jnz poping		;Продолжаем процесс, пока в cl не будет 0
	
	add buf[1], 01h	;Увеличиваем кол-во элементов в буфере на 1 для символа $
	mov buf[bp],24h ;Помещаем в буфер символ конца строки
	jmp final

emptyAx:
	mov buf[1], 02h		;2 элемента в буфере: 0 и $
	mov buf[2], 30h		;ASCII код 0
	mov buf[3], 24h		;Помещаем символ конца строки на последнее место в буфере

final:
	ret
convertIntToStr endp
Помогите найти ошибку.
Сообщение об ошибке:
Процессор NTVDM обнаружил недопустимую инструкцию.
CS:0536 IP:0007 OP: fe 1d f0 de 01

Без этой процедуры никаких ошибок нет. В чем может быть проблема?
Заранее спасибо.




___________
Код нужно оформлять по правилам:
тегом [CODE]..[/СODE] (это кнопочка с решёточкой #)
Не забывайте об этом!
Модератор.

Последний раз редактировалось Serge_Bliznykov; 10.04.2012 в 15:04.
Glaciuse вне форума Ответить с цитированием
Старый 10.04.2012, 15:04   #2
Serge_Bliznykov
Старожил
 
Регистрация: 09.01.2008
Сообщений: 26,229
По умолчанию

а как вызываете?
и вообще, давайте исходный код программы целиком.
Serge_Bliznykov вне форума Ответить с цитированием
Старый 10.04.2012, 15:11   #3
Glaciuse
Пользователь
 
Регистрация: 22.05.2011
Сообщений: 44
По умолчанию

Вот весь код: (он еще не доработан и я только начал изучать, не судите строго)
Код:
;Программа вычисления выражения: (A*B-C)/D
; A,B,C,D : unsigned byte

codesg segment para "code" ;Начало сегмента кода
assume cs:codesg, ds:codesg, ss:codesg, es:codesg
org 100h ;Начало программы в конце PSP

start:
	jmp main 	;Обход через данные

;Начало инициализации данных
	buf db 10 dup(0) 	;буфер ввода 10 байт, заполненных 0
	
	;Инициализируем переменные
	ubyteA  db 0
	ubyteB	db 0
	ubyteC	db 0
	ubyteD	db 0
	uWordTemp dw 0
	;Конец инициализации переменных
	
	;Инициализируем сообщения об ошибках
	notANumberMessage db "Entered value is not a number!$"
	negativeNumberMassage db "Entered nagative number! Only possitive are allowed.$"
	tooBigNumberMassage db "Overflow bit grid.$"
	devideBy0 db "Excuse me, do you really going to divide by 0? $"
	;Конец инициализации сообщений об ошибках
	
	;Инициализируем информационные сообщения
	programTitleMessage db "This program evaluates the expression: (A*B-C)/D$"
	variablesInfoMessage db "The input variables should be in the range 0..255$"
	promptForInputMessage db "Enter the value of variables A, B, C, D :$"
	resultMessage db "Result = $"
	aMessage db "A = $"
	bMessage db "B = $"
	cMessage db "C = $"
	dMessage db "D = $"
	dotSymbol db ".$"
	;Конец инициализации информационных сообщений
	
;Конец инициализации данных

main proc near		;Начало главной процедуры

	mov  buf[0],6     	;Определим размер входного буфера (пишем в max)
						; Так выглядит буфер в памяти: max¦len¦T¦E¦X¦T¦0Dh
	
	call cls		;Очищаем экран
	
	;Вывод 3х сообщений на экран (О программе, о вводимых данных, приглашение на ввод)
	lea dx, programTitleMessage
	call writeMessageFromDx
	call newLine
	lea dx, variablesInfoMessage 
	call writeMessageFromDx
	call newLine
	lea dx, promptForInputMessage
	call writeMessageFromDx
	call newLine
	;Конец вывода 3х сообщений
	
	;Ввод переменной А
	lea dx, aMessage	 	;Записываем адрес сообщения "А = " в DX
	call writeMessageFromDx	;Выводим это сообщение
	lea dx, buf				;Записываем в DX адрес буфера
	call scan				;Считываем строку из консоли в буфер по адресу dx
	call convertStrToInt	;Конвертируем введенную строку в uByte с провверкой, результат в al
	mov uByteA, al			;Записываем результат в переменную
	call newLine
	;Конец ввода переменной А
	
	;Ввод переменной B
	lea dx, bMessage	 	;Записываем адрес сообщения "B = " в DX
	call writeMessageFromDx	;Выводим это сообщение
	lea dx, buf				;Записываем в DX адрес буфера
	call scan				;Считываем строку из консоли в буфер по адресу dx
	call convertStrToInt	;Конвертируем введенную строку в uByte с провверкой, результат в al
	mov uByteB, al			;Записываем результат в переменную
	call newLine
	;Конец ввода переменной B
	
	;Ввод переменной C
	lea dx, cMessage	 	;Записываем адрес сообщения "С = " в DX
	call writeMessageFromDx	;Выводим это сообщение
	lea dx, buf				;Записываем в DX адрес буфера
	call scan				;Считываем строку из консоли в буфер по адресу dx
	call convertStrToInt	;Конвертируем введенную строку в uByte с провверкой, результат в al
	mov uByteC, al			;Записываем результат в переменную
	call newLine
	;Конец ввода переменной C
	
	;Ввод переменной D
	lea dx, dMessage	 	;Записываем адрес сообщения "D = " в DX
	call writeMessageFromDx	;Выводим это сообщение
	lea dx, buf				;Записываем в DX адрес буфера
	call scan				;Считываем строку из консоли в буфер по адресу dx
	call convertStrToInt	;Конвертируем введенную строку в uByte с провверкой, результат в al
	mov ah, al				;Проверяем, чтобы небыло деления на 0
	jz writeVar
	lea dx, devideBy0		;Заносим в DX сообщение о том, что делить на 0 нельзя
	call writeMessageFromDx	;Выводим это сообщение
	call newLine
	call exit
writeVar:
	mov uByteD, al			;Записываем результат в переменную
	call newLine
	;Конец ввода переменной D
	
	lea dx, resultMessage	;Заносим в DX сообщение "Result = "
	call writeMessageFromDx	;Выводим это сообщение
	
	call calculate			;Процедура, вычисляющая вырежение, результат в AX, остаток в DX

	mov uWordTemp, dx		;Сохраняем остаток во временной переменной
	
	call convertIntToStr	;Процедура преобразования числа в AX в строку символов, хранящаюся в buf
	
	lea dx, buf				;Помещаем в DX адрес буфера
	add dl, 02h				;Указываем на начало строки в буфере

	call writeMessageFromDx	;Выводм строку, адрес начала которой в DX

	lea dx, dotSymbol	 	;Записываем адрес сообщения "D = " в DX
	call writeMessageFromDx	;Выводим это сообщение

	mov ax, uWordTemp		;Записываем в AX остаток при вычислении выражения

	call convertIntToStr	;Переводим остаток в строковый тип
	
	lea dx, buf				;Помещаем в DX адрес буфера
	add dl, 02h				;Указываем на начало строки в буфере

	call writeMessageFromDx	;Выводм строку, адрес начала которой в DX
	
	call exit
	ret
main endp			;Конец главной процедуры
Glaciuse вне форума Ответить с цитированием
Старый 10.04.2012, 15:12   #4
Glaciuse
Пользователь
 
Регистрация: 22.05.2011
Сообщений: 44
По умолчанию

Код:
cls proc near		;Процедура очистки экрана

	push ax	
	push bx
	push cx
	push dx
	
	xor al, al	;Очистка окна при al = 0
	mov ah, 6	;Вызов функции scroll_up
	mov bh, 7	;Нормальные атрибуты очистки
	xor cx, cx	;При cx = 0, координаты курсора будут (0,0)
	mov dh, 24	;Устанавливаем максимальное кол-во строк в консоли
	mov dl, 79	;Устанавливаем максимальное кол-во столбцов в консоли

	int 10h 	;Запускаем прерывание
	
	pop dx
	pop cx
	pop bx	
	pop ax
	
	ret		;Выходим из процедуры
cls endp			;Конец процедуры очистки экрана

writeMessageFromDx proc near ;Процедура вывода строки по адресу в DX на экран
	push ax
	mov ah, 09h
	int 21h			;Запуск прерывания для вывода строки на экран
	pop ax
	ret
writeMessageFromDx endp ;Конец процедуры вывода на экран

newLine proc near
	push ax
	push bx
	push cx
	
	mov bh, 00h
	mov cx, 01h
	mov al, 0Dh		;Перевод строки
	mov ah, 0Eh		;режим отображения TTY (Работают управляющие коды)
	int 10h			;Запускаем прерывание
	
	mov al, 0Ah		;Возврат каретки
	mov ah, 0Eh		;режим отображения TTY (Работают управляющие коды)
	int 10h			;Запускаем прерывание 
	
	pop cx
	pop bx
	pop ax
	ret
newLine endp

scan proc near 	;Процедура считывания строки из консоли и записывающая результат в буфер, адрес которого в DX
	push ax
	mov ah,0Ah
	int 21h
	pop ax
	ret
scan  endp

convertStrToInt proc near ;Процедура преобразования строки в int, результат в dl

	;Записываем количество символов в буфере в cl (адрес это числа = адрес буфера + 2)
	;Запускаем цикл пока количество прочитанных символов не будет = 0
	;На каждом шаге цикла проверяем код символа чтобы был в диапазоне от 30 до 39 (0..9)
	;Если считываемый символ находится не в диапазоне 30..39, то выводим сообщение об ошибке ввода
	;Иначе вычитаем из номера символа 30h и получаем нужный символ
	xor ax, ax
	push bx
	push bp
	push cx
	
	mov bh, 0Ah		;На что будем умножать считанное число, чтобы привести к нужному порядку
	
	mov cl, buf[1]	;Записываем в cl количество символов в буфере
	mov bp, 02h 	;Текущий элемент буфера
	mov al, buf[bp] ;Записываем в ah первый символ в буфере (возможно это знак)
	cmp al, 2Dh		;Сравниваем первый символ буфера со знаком -
	je negativeNumber	;Переходим, если первый знак -
	cmp al, 2Bh			;Сравниваем первый символ буфера со знаком +
	jne cycle	;Если первый симво в буфере не +, начинаем конвертацию
	dec cl			;Уменьшаем кол-во проситанных символов на 1
	inc bp			;Увеличиваем bp для рассмотрения следующего символа

cycle:
	mov al, buf[bp]	;Читаем в al элемент буфера под номером bp
	;Проверям чтобы символ в буфере был между 0 и 9
	cmp al, 30h	;Проверяем на < 0
	jl notANumber
	cmp al, 39h 	;Проверяем на > 9
	jg notANumber
	
	;Раз дошли до сюда, значит в буфере под номером bp (то, что сейчас в al) все же число	
	sub al, 30h 	;Вычитаем из al 30h, чтобы получить 10-ую цифру
	
	mov bl, cl		;Помещаем в bl количество символов для установки конца цикла умножения
	sub bl, 01h		;Уменьшаем на 1, т.к. при 3х символах, надо умножить 2 раза на 10
	jz theLastOne
convert:	
	mul bh			;Умножаем al на bh пока пока bl не будет = 0
	jo tooBigNumber
	dec bl
	jnz convert
theLastOne:	
	inc bp 			;Увеличиваем указатель для чтения следующего элемента массива
	add ch, al		;ch хранит промежуточный результат
	jc tooBigNumber	;Если произошло переполнение, выводим сообщение об ошибке	
	dec cl
	jnz cycle
	jmp done
	
notANumber:
        call newLine
        lea dx, notANumberMessage
        call writeMessageFromDx
        call exit
	
negativeNumber:
	call newLine
	lea dx, negativeNumberMassage
        call writeMessageFromDx
	call exit
	
tooBigNumber:
	call newLine
	lea dx, tooBigNumberMassage
        call writeMessageFromDx
	call exit
	
done:
	mov al, ch ; Записываем полученное число в al
	pop cx
	pop bp
	pop bx
	ret
convertStrToInt endp

Последний раз редактировалось Glaciuse; 10.04.2012 в 15:27.
Glaciuse вне форума Ответить с цитированием
Старый 10.04.2012, 15:12   #5
Glaciuse
Пользователь
 
Регистрация: 22.05.2011
Сообщений: 44
По умолчанию

Код:
calculate proc near
	xor ax, ax
	xor dx, dx
	push bx
	
	mov al, uByteA		;Записываем в al переменную А
	mul uByteB		;Умножаем переменную А в al на переменную B, результат в AX
	cmp ah, 80h		;Проверим старший бит
	jb	positiveResult	;Если он 0, то продолжаем	
	mov cl, 01h		;Если при умножении получилось число, старший бит которого = 1, запоминаем это в cl
positiveResult:
	mov bl, uByteC
	sub ax, bx		;Вычитаем из полученного результата переменную C (при этом может получиться отрицательное число)
	cmp ah, 80h		;Проверим старший бит
	jb continue		;Если получившееся число < 8000
	;Попадаем сюда, если при вычитании получилось отрицательное число
	mov ch, cl
	jz continue			;Если при умножении получилось "не отрицательное" число, то переходим
	mov ch, 01h			;В ch храниться знак числа
	neg ax
continue:
	xor bx, bx
	mov bl, uByteD	
			
	div bx 	;Делим DX:AX на BX
	
	pop bx
	ret
calculate endp

convertIntToStr proc near ;Процедура преобразования int в ASCII, преобразуемое число в AX, знак числа в CH

	mov buf[1], 00h ;Устанавилваем кол-во элементов в буфере
	mov bp, 02h 	;Указатель на 1й элемент массива
	mov bx, 0Ah		;Записываем в AX основание системы счисления, на которую будем делить
	cmp ax, 00h		;Если в AX - 0, то прыгаем на emptyAx
	je emptyAx
	
	cmp ch, 01h		;Проверяем что за знак в ch (0 - '+', 1 - '-')
	jne pushing		;Прыгаем, если в ch = 0
	neg ax			;Если в ch была 1, то инвертируем AX
	add buf[1], 01h	;Увеличиаем кол-во лементов в буфере на 1 , т.к. будет знак '-'
	mov buf[2], 2Dh	;Помещаем в 1 ячейку буфера знак - 
	add bp, 01h		;Увеличиваем указатель на 1й элемент массива

pushing:
	xor dx, dx		;Очищаем регистр DX, т.к. там может находиться остаток от деления
	div bx			;Делим AX на BX
	push dx			;Записываем остаток в стек
	inc cl			;Увеличиваем кол-во записыванных символов в стек
	
	cmp ax, 00h 	;Сравниваем АХ с 0
	je poping		;Если он равен 0, то переходим (деление закончено)
	jmp pushing		;Иначе продожаем деление

poping:
	pop dx			;Вытаскиваем верхний символ из стека
	add dx, 30h		;Добавляем 30,ч тобы получить ASCII код
	add buf[1], 01h	;Увеличиваем кол-во элементов в буфере на 1
	mov buf[bp], dl	;Помещаем ASCII символ в буфер
	inc bp			;Увеличиваем указатель на след элемент буфера
	dec cl			;Уменьшаем счетчик кол-ва элекментов в стеке
	jnz poping		;Продолжаем процесс, пока в cl не будет 0
	
	add buf[1], 01h	;Увеличиваем кол-во элементов в буфере на 1 для символа $
	mov buf[bp],24h ;Помещаем в буфер символ конца строки
	jmp final

emptyAx:
	mov buf[1], 02h		;2 элемента в буфере: 0 и $
	mov buf[2], 30h		;ASCII код 0
	mov buf[3], 24h		;Помещаем символ конца строки на последнее место в буфере

final:
	ret
convertIntToStr endp

exit proc
	int 20h
exit endp

codesg  ends	
end start
Glaciuse вне форума Ответить с цитированием
Старый 10.04.2012, 17:07   #6
Glaciuse
Пользователь
 
Регистрация: 22.05.2011
Сообщений: 44
По умолчанию

Работаю с TASM, TLINK, TD.
Если через TD прогу запускать, то все ок, а из cmd.exe не хочет, мб система кривая, а не прога?
Glaciuse вне форума Ответить с цитированием
Старый 10.04.2012, 21:14   #7
s-andriano
Старожил
 
Аватар для s-andriano
 
Регистрация: 08.04.2012
Сообщений: 3,229
По умолчанию

Цитата:
Сообщение от Glaciuse Посмотреть сообщение
Работаю с TASM, TLINK, TD.
Если через TD прогу запускать, то все ок, а из cmd.exe не хочет, мб система кривая, а не прога?
Обычно это признаки того, что в программе не инициализируются (не обнуляются?) нужные переменные.
s-andriano вне форума Ответить с цитированием
Старый 10.04.2012, 22:07   #8
Serge_Bliznykov
Старожил
 
Регистрация: 09.01.2008
Сообщений: 26,229
По умолчанию

Цитата:
Сообщение от s-andriano
Обычно это признаки того, что в программе не инициализируются (не обнуляются?) нужные переменные.
+1

тоже сталкивался с подобным.
но сейчас найти косяк не удалось - в отладчике всё работает как часы!

Glaciuse, а проверка деления на ноль, имхо, у вас некорректно реализована!
я бы так предложил написать (то, что красным - убрать, то, что зелёным - добавить:
Код:
        call convertStrToInt    ;Конвертируем введенную строку в uByte с провверкой, результат в al
;        mov ah, al                              ;Проверяем, чтобы небыло деления на 0
;        jz writeVar
        cmp al, 00h                             ;Проверяем, чтобы не было деления на 0  
        jnz writeVar
Serge_Bliznykov вне форума Ответить с цитированием
Старый 11.04.2012, 03:57   #9
Ulex
Непрофессионал
Участник клуба
 
Аватар для Ulex
 
Регистрация: 01.01.2008
Сообщений: 1,405
По умолчанию

То что бросилось в глаза. В процедуре convertIntToStr, изначально cl не занулено, там скорее всего какой-то мусор, а потом вы по этому мусору начинаете работать со стеком, цикл с попами. По крайней мере лично у меня программа вообще ничего не выводила, пока я там со счётчиками порядок не навёл.

Вот этот выриант работает, по крайней мере под NTVDM и под DOSBOX.
Вложения
Тип файла: rar proga70.rar (4.6 Кб, 7 просмотров)
И чем больше я узнавал людей, тем больше мне нравились компьютеры.
------------------------------------
Страничка с моими программками http://ulex-masm.ru
Ulex вне форума Ответить с цитированием
Старый 11.04.2012, 08:22   #10
Serge_Bliznykov
Старожил
 
Регистрация: 09.01.2008
Сообщений: 26,229
По умолчанию

Ulex, браво!!

в принципе, в исходный код достаточно зануление cx добавить, чтобы всё заработало:
Код:
convertIntToStr proc near ;Процедура преобразования int в ASCII, преобразуемое число в AX, знак числа в CH

        xor cx, cx
        mov buf[1], 00h ;Устанавилваем кол-во элементов в буфере


Добавлено
посмотрел код (с) Ulex - да он там полпрограммы перелопал!!
Очень рекомендую взять его вариант!

Последний раз редактировалось Serge_Bliznykov; 11.04.2012 в 08:29.
Serge_Bliznykov вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Процессор NTVDM обнаружил недопустимую инструкцию (глючит Паскаль?) hon Паскаль, Turbo Pascal, PascalABC.NET 2 02.08.2011 13:41
Описать код программы и написать инструкцию (=Анастасия=) Помощь студентам 0 20.06.2011 18:11
Может вы знаете что делать если 'процессор NTVDM обнаружил недопустимую инструкцию' aslanbek999 Помощь студентам 1 04.06.2009 00:26
Ошибка: Процессор NTVDM обнаружил недопустимою инструкцию. Iceman Паскаль, Turbo Pascal, PascalABC.NET 4 23.03.2009 14:32