; (c) xrnd [2010] [http://asmworld.ru/]
; inputdec.asm - ввод десятичных чисел (со знаком и без знака)

use16			    ;Генерировать 16-битный код
org 100h		    ;Программа начинается с адреса 100h

    mov cx,print_str	    ;Адрес процедуры print_str
    mov bx,print_endline    ;Адрес процедуры print_endline

in_ubyte_lp:
    mov di,s_ubyte
    call cx		    ;Вывод строки 'unsigned byte:'
    call input_udec_byte    ;Ввод байта без знака
    call bx		    ;Вывод конца строки
    jnc in_uword_lp	    ;Если нет ошибки, переход к вводу слова без знака
    mov di,s_error
    call cx		    ;Вывод строки 'ERROR!'
    jmp in_ubyte_lp	    ;Переход к началу цикла

in_uword_lp:
    mov di,s_uword
    call cx		    ;Вывод строки 'unsigned word:'
    call input_udec_word    ;Ввод слова без знака
    call bx		    ;Вывод конца строки
    jnc in_sbyte_lp	    ;Если нет ошибки, переход к вводу байта со знаком
    mov di,s_error
    call cx		    ;Вывод строки 'ERROR!'
    jmp in_uword_lp	    ;Переход к началу цикла

in_sbyte_lp:
    mov di,s_sbyte
    call cx		    ;Вывод строки 'signed byte :'
    call input_sdec_byte    ;Ввод байта со знаком
    call bx		    ;Вывод конца строки
    jnc in_sword_lp	    ;Если нет ошибки, переход к вводу слова со знаком
    mov di,s_error
    call cx		    ;Вывод строки 'ERROR!'
    jmp in_sbyte_lp	    ;Переход к началу цикла

in_sword_lp:
    mov di,s_sword
    call cx		    ;Вывод строки 'signed word :'
    call input_sdec_word    ;Ввод слова со знаком
    call bx		    ;Вывод конца строки
    jnc exit		    ;Если нет ошибки, переход к выходу из программы
    mov di,s_error
    call cx		    ;Вывод строки 'ERROR!'
    jmp in_sword_lp	    ;Переход к началу цикла

exit:
    mov di,s_pak
    call cx		    ;Вывод строки 'Press any key...'
    mov ah,8		    ;Функция DOS 08h - ввод символа без эха
    int 21h

    mov ax,4C00h	    ;\
    int 21h		    ;/ Завершение программы

;-------------------------------------------------------------------------------
;Процедура ввода байта с консоли в десятичном виде (без знака)
; выход: AL - байт (в случае ошибки AL = 0)
;        CF = 1 - ошибка
input_udec_byte:
    push dx		    ;Сохранение DX
    mov al,4		    ;Ввод максимум 3 символов (255) + конец строки
    call input_str	    ;Вызов процедуры ввода строки
    call str_to_udec_byte   ;Преобразование строки в байт (без знака)
    pop dx		    ;Восстановление DX
    ret

;-------------------------------------------------------------------------------
;Процедура ввода слова с консоли в десятичном виде (без знака)
; выход: AX - слово (в случае ошибки AX = 0)
;        CF = 1 - ошибка
input_udec_word:
    push dx		    ;Сохранение DX
    mov al,6		    ;Ввод максимум 5 символов (65535) + конец строки
    call input_str	    ;Вызов процедуры ввода строки
    call str_to_udec_word   ;Преобразование строки в слово (без знака)
    pop dx		    ;Восстановление DX
    ret

;-------------------------------------------------------------------------------
;Процедура ввода байта с консоли в десятичном виде (со знаком)
; выход: AL - байт (в случае ошибки AL = 0)
;        CF = 1 - ошибка
input_sdec_byte:
    push dx		    ;Сохранение DX
    mov al,5		    ;Ввод максимум 3 символов (-128) + конец строки
    call input_str	    ;Вызов процедуры ввода строки
    call str_to_sdec_byte   ;Преобразование строки в байт (со знаком)
    pop dx		    ;Восстановление DX
    ret

;-------------------------------------------------------------------------------
;Процедура ввода слова с консоли в десятичном виде (со знаком)
; выход: AX - слово (в случае ошибки AX = 0)
;        CF = 1 - ошибка
input_sdec_word:
    push dx		    ;Сохранение DX
    mov al,7		    ;Ввод максимум 7 символов (-32768) + конец строки
    call input_str	    ;Вызов процедуры ввода строки
    call str_to_sdec_word   ;Преобразование строки в слово (со знаком)
    pop dx		    ;Восстановление DX
    ret

;-------------------------------------------------------------------------------
;Процедура преобразования десятичной строки в байт без знака
;  вход: AL - длина строки
;        DX - адрес строки, заканчивающейся символом CR(0Dh)
; выход: AL - байт (в случае ошибки AL = 0)
;        CF = 1 - ошибка
str_to_udec_byte:
    push dx		    ;Сохранение регистров
    push ax
    call str_to_udec_word   ;Преобразование строки в слово (без знака)
    jc studb_exit	    ;Если ошибка, то возвращаем ошибку
    test ah,ah		    ;Проверка старшего байта AX
    jz studb_exit	    ;Если 0, то выход из процедуры (здесь всегда CF = 0)
    xor al,al		    ;AL = 0
    stc 		    ;CF = 1 (Возвращаем ошибку)
studb_exit:
    pop dx
    mov ah,dh		    ;Восстановление только старшей части AX
    pop dx
    ret

;-------------------------------------------------------------------------------
;Процедура преобразования десятичной строки в слово без знака
;  вход: AL - длина строки
;        DX - адрес строки, заканчивающейся символом CR(0Dh)
; выход: AX - слово (в случае ошибки AX = 0)
;        CF = 1 - ошибка
str_to_udec_word:
    push cx		    ;Сохранение всех используемых регистров
    push dx
    push bx
    push si
    push di

    mov si,dx		    ;SI = адрес строки
    mov di,10		    ;DI = множитель 10 (основание системы счисления)
    movzx cx,al 	    ;CX = счётчик цикла = длина строки
    jcxz studw_error	    ;Если длина = 0, возвращаем ошибку
    xor ax,ax		    ;AX = 0
    xor bx,bx		    ;BX = 0

studw_lp:
    mov bl,[si] 	    ;Загрузка в BL очередного символа строки
    inc si		    ;Инкремент адреса
    cmp bl,'0'		    ;Если код символа меньше кода '0'
    jl studw_error	    ; возвращаем ошибку
    cmp bl,'9'		    ;Если код символа больше кода '9'
    jg studw_error	    ; возвращаем ошибку
    sub bl,'0'		    ;Преобразование символа-цифры в число
    mul di		    ;AX = AX * 10
    jc studw_error	    ;Если результат больше 16 бит - ошибка
    add ax,bx		    ;Прибавляем цифру
    jc studw_error	    ;Если переполнение - ошибка
    loop studw_lp	    ;Команда цикла
    jmp studw_exit	    ;Успешное завершение (здесь всегда CF = 0)

studw_error:
    xor ax,ax		    ;AX = 0
    stc 		    ;CF = 1 (Возвращаем ошибку)

studw_exit:
    pop di		    ;Восстановление регистров
    pop si
    pop bx
    pop dx
    pop cx
    ret

;-------------------------------------------------------------------------------
;Процедура преобразования десятичной строки в байт со знаком
;  вход: AL - длина строки
;        DX - адрес строки, заканчивающейся символом CR(0Dh)
; выход: AL - байт (в случае ошибки AL = 0)
;        CF = 1 - ошибка
str_to_sdec_byte:
    push dx		    ;Сохранение регистров
    push ax
    call str_to_sdec_word   ;Преобразование строки в слово (со знаком)
    jc stsdb_exit	    ;Если ошибка, то возвращаем ошибку
    cmp ax,127		    ;Сравнение результата с 127
    jg stsdb_error	    ;Если больше - ошибка
    cmp ax,-128 	    ;Сравнение результата с -128
    jl stsdb_error	    ;Если меньше - ошибка
    clc 		    ;CF = 0
    jmp studb_exit	    ;Переход к выходу из процедуры
stsdb_error:
    xor al,al		    ;AL = 0
    stc 		    ;CF = 1 (Возвращаем ошибку)
stsdb_exit:
    pop dx
    mov ah,dh		    ;Восстановление только старшей части AX
    pop dx
    ret

;-------------------------------------------------------------------------------
;Процедура преобразования десятичной строки в слово со знаком
;  вход: AL - длина строки
;        DX - адрес строки, заканчивающейся символом CR(0Dh)
; выход: AX - слово (в случае ошибки AX = 0)
;        CF = 1 - ошибка
str_to_sdec_word:
    push bx		    ;Сохранение регистров
    push dx

    test al,al		    ;Проверка длины строки
    jz stsdw_error	    ;Если равно 0, возвращаем ошибку
    mov bx,dx		    ;BX = адрес строки
    mov bl,[bx] 	    ;BL = первый символ строки
    cmp bl,'-'		    ;Сравнение первого символа с '-'
    jne stsdw_no_sign	    ;Если не равно, то преобразуем как число без знака
    inc dx		    ;Инкремент адреса строки
    dec al		    ;Декремент длины строки
stsdw_no_sign:
    call str_to_udec_word   ;Преобразуем строку в слово без знака
    jc stsdw_exit	    ;Если ошибка, то возвращаем ошибку
    cmp bl,'-'		    ;Снова проверяем знак
    jne stsdw_plus	    ;Если первый символ не '-', то число положительное
    cmp ax,32768	    ;Модуль отрицательного числа должен быть не больше 32768
    ja stsdw_error	    ;Если больше (без знака), возвращаем ошибку
    neg ax		    ;Инвертируем число
    jmp stsdw_ok	    ;Переход к нормальному завершению процедуры
stsdw_plus:
    cmp ax,32767	    ;Положительное число должно быть не больше 32767
    ja stsdw_error	    ;Если больше (без знака), возвращаем ошибку

stsdw_ok:
    clc 		    ;CF = 0
    jmp stsdw_exit	    ;Переход к выходу из процедуры
stsdw_error:
    xor ax,ax		    ;AX = 0
    stc 		    ;CF = 1 (Возвращаем ошибку
stsdw_exit:
    pop dx		    ;Восстановление регистров
    pop bx
    ret

;-------------------------------------------------------------------------------
;Процедура ввода строки c консоли
;  вход: AL - максимальная длина (с символом CR) (1-254)
; выход: AL - длина введённой строки (не считая символа CR)
;        DX - адрес строки, заканчивающейся символом CR(0Dh)
input_str:
    push cx		    ;Сохранение СX
    mov cx,ax		    ;Сохранение AX в CX
    mov ah,0Ah		    ;Функция DOS 0Ah - ввод строки в буфер
    mov [buffer],al	    ;Запись максимальной длины в первый байт буфера
    mov byte[buffer+1],0    ;Обнуление второго байта (фактической длины)
    mov dx,buffer	    ;DX = aдрес буфера
    int 21h		    ;Обращение к функции DOS
    mov al,[buffer+1]	    ;AL = длина введённой строки
    add dx,2		    ;DX = адрес строки
    mov ah,ch		    ;Восстановление AH
    pop cx		    ;Восстановление CX
    ret

;-------------------------------------------------------------------------------
;Процедура вывода строки на консоль
; DI - адрес строки
print_str:
    push ax
    mov ah,9		    ;Функция DOS 09h - вывод строки
    xchg dx,di		    ;Обмен значениями DX и DI
    int 21h		    ;Обращение к функции DOS
    xchg dx,di		    ;Обмен значениями DX и DI
    pop ax
    ret

;-------------------------------------------------------------------------------
;Процедура вывода конца строки (CR+LF)
print_endline:
    push di
    mov di,endline	    ;DI = адрес строки с символами CR,LF
    call print_str	    ;Вывод строки на консоль
    pop di
    ret

;-------------------------------------------------------------------------------
; Данные
s_ubyte  db 'unsigned byte: $'
s_uword  db 'unsigned word: $'
s_sbyte  db 'signed byte  : $'
s_sword  db 'signed word  : $'
s_error  db 'ERROR!',13,10,'$'
s_pak	 db 'Press any key...$'
endline  db 13,10,'$'
buffer	 rb 9
