algorithmassemblyencryptionasciitasm

Struggling with TEA Algorithm in Assembly 8086 TASM


I’m attempting to implement the TEA algorithm in assembly language (8086 TASM). However, I’m encountering difficulties in two areas

  1. String Input: I need assistance in implementing a mechanism to input strings from the user within my assembly program.

  2. Hexadecimal Conversion: After obtaining user input, I aim to convert each letter of the input string into its corresponding hexadecimal representation to prepare it for TEA encryption. I’m seeking guidance on how to accomplish this conversion efficiently.

  3. Encrypted Message Decryption: Following encryption using the TEA algorithm, I’m encountering difficulty in converting the resulting encrypted message back into readable string letters. I require assistance in decrypting and converting the encrypted message into its original form.

I’ve successfully implemented the encrypt and decrypt functions for the TEA algorithm. These functions seem to perform encryption and decryption operations correctly. However, I’m stuck at handling string input and converting them into a format suitable for encryption. Additionally, after encryption, the output contains unknown symbols, and I need assistance in converting it into readable encrypted strings.

Here is my encrypt / decrypt functions:

proc encrypt
    pusha

    mov cx, 32
    encryptLoop:
        ; sum += delta
        mov ax, [delta]
        add [sum], ax

        xor eax,eax
        ; ((p2 << 4) + k0)
        mov dx, [p2]
        shl dx, 4
        add dx, [k0]

        ; (p2 + sum)
        mov bx, [p2]
        add bx, [sum]

        ; ((p2 << 4) + k0) ^ (p2 + sum)
        xor dx, bx

        xor bx, bx

        ; ((p2 >> 5) + k1)
        mov bx, [p2]
        shr bx, 5
        add bx, [k1]
        ; ((p2 << 4) + k0) ^ (p2 + sum) ^ ((p2 >> 5) + k1)
        xor dx, bx
        add [pe1], dx

        ; ((p1 << 4) + k2)
        mov dx, [p1]
        shl dx, 4
        add dx, [k2]

        ; (p1 + sum)
        mov bx, [p1]
        add bx, [sum]

        ; ((p0 << 4) + k2) ^ (p0 + sum)
        xor dx, bx

        xor bx, bx

        ; ((p1 >> 5 ) + k3)
        mov bx,[p1]
        shr bx, 5
        add bx, [k3] 
        ; ((p0 << 4) + k2) ^ (p0 + sum) ^ ((p1 >> 5 ) + k3)
        xor dx, bx

        add [pe2], dx
    loop encryptLoop

    popa
    ret
endp encrypt

proc decrypt
   pusha

    mov cx, 32
    decryptLoop:
        ; ((p1 << 4) + k2)
        mov dx, [p1]
        shl dx, 4
        add dx, [k2]

        ; (p1 + sum)
        mov bx, [p1]
        add bx, [sum]

        ; ((p0 << 4) + k2) ^ (p0 + sum)
        xor dx, bx

        xor bx, bx

        ; ((p1 >> 5 ) + k3)
        mov bx,[p1]
        shr bx, 5
        add bx, [k3] 
        ; ((p0 << 4) + k2) ^ (p0 + sum) ^ ((p1 >> 5 ) + k3)
        xor dx, bx

        sub [pe2], dx

        xor ax,ax
        ; ((p2 << 4) + k0)
        mov dx, [p2]
        shl dx, 4
        add dx, [k0]

        ; (p2 + sum)
        mov bx, [p2]
        add bx, [sum]
        
        ; ((p1 << 4) + k0) ^ (p1 + sum)
        xor dx, bx

        xor bx, bx

        ; ((p2 >> 5) + k1)
        mov bx, [p2]
        shr bx, 5
        add bx, [k1]
        ; ((p2 << 4) + k0) ^ (p2 + sum) ^ ((p2 >> 5) + k1)
        xor dx, bx
        sub [pe1], dx

        ; sum -= delta
        mov ax, [delta]
        sub [sum], ax
    loop decryptLoop

    popa

    ret
endp decrypt

DS section:

delta dw 79b9h ; 79b9h for 16 bits, 9e3779b9h for 32 bits

sum dw 0
p1 dw 0
p2 dw 0

k0  dw 0                  
k1  dw 0                   
k2  dw 0                   
k3  dw 0         

; Two part of the plaintext after encryption
pe1 dw 0
pe2 dw 0        

Solution

  • Edit: Hope I understood this algorithm correctly. ;)


    First answer:

    Memory looks like this before calling to encryption:

    Delta: 9e3779b9h
    p1   : 20656349h = 'Ice '
    p2   : 21414554h = 'TEA!'
    
    k1   : 00000016h = 22
    k2   : 0000000bh = 11
    k3   : 00000021h = 33
    k4   : 00000003h =  3
    
    -d ds:0
    0EED:0000  B9 79 37 9E 00 00 00 00-49 63 65 20 54 45 41 21 .y7.....Ice TEA!
    0EED:0010  16 00 00 00 0B 00 00 00-21 00 00 00 03 00 00 00 ........!.......
    

    After encryption:

    Delta: 9e3779b9h
    p1   : c6ef3720h (encrypted)
    p2   : c96bcfebh (encrypted)
    
    k1   : 00000016h = 22
    k2   : 0000000bh = 11
    k3   : 00000021h = 33
    k4   : 00000003h =  3
    
    -d ds:0
    0EED:0000  B9 79 37 9E 20 37 EF C6-7A D0 EA DC 1E FC 4C 9A .y7. 7..z.....L.
    0EED:0010  16 00 00 00 0B 00 00 00-21 00 00 00 03 00 00 00 ........!.......
    

    Now decryption, back to normal:

    Delta: 9e3779b9h
    p1   : 20656349h = 'Ice '
    p2   : 21414554h = 'TEA!'
    
    k1   : 00000016h = 22
    k2   : 0000000bh = 11
    k3   : 00000021h = 33
    k4   : 00000003h =  3
    
    -d ds:0
    0EED:0000  B9 79 37 9E 00 00 00 00-49 63 65 20 54 45 41 21 .y7.....Ice TEA!
    0EED:0010  16 00 00 00 0B 00 00 00-21 00 00 00 03 00 00 00 ........!.......
    

    I used hardcoded values for p1,p2,k0,k1,k2,k3.


    Update 1:

    I added:

    print_text proc - shows string in console, string offset should be in si

    user_input proc - takes data from user

    check_chars proc - we want only right chars

    plain_txt_buffer size is 32 bytes + 1 byte for 0dh.

    If user enters 32 chars 0dh will be added automatically at the end of the buffer.


    Update 2:

    input_flag inside user_input proc and check_chars controls which data set is used:

     1 - data set for plain_text , 20h - 7fh, enter, backspace
    
     2 - data set for keys, only digits 30h - 39h, enter, backspace
    

    keys_buffer size is 10 bytes + 1 byte for 0dh.

    If user enters 10 chars 0dh will be added automatically at the end of the buffer.


    Update 3:


    Update 4:


    Here is the updated code:

    .386
    ideal
    model small
    stack 256
    segment datasg
    
        plain_txt_buffer db 33 dup(0)                   ; store user's text     
        pt_buffer_size   db $ - plain_txt_buffer - 1    ; size of the buffer without 0dh
        
        keys_buffer      db 11 dup(0)                   ; store user's keys 
        k_buffer_size    db $ - keys_buffer - 1         ; size of the buffer without 0dh
    
        enc_buffer       db 32 dup(0)                   ; encrypted txt buffer
        ;enc_buffer_size  db $ - enc_buffer             ; not used in the code
    
        dec_buffer       db 32 dup(0)                   ; decrypted txt buffer
        ;dec_buffer_size  db $ - dec_buffer             ; not used in the code
    
        chars_in_buffer  db 0                           ; how many chars user entered, used in key32bit proc
    
        input_flag       dw 1                           ; 1 - plain text data set, [20h ... 7fh], enter, backspace
                                                        ; 2 - keys data set, only digits, enter, backspace
    
        delta dd 9e3779b9h                              ; 9e3779b9h for 32 bits
    
        sum dd 0        
        p1 dd 20656349h
        p2 dd 21414554h
        
        k0  dd 65536                                    ; [0 ... 4,294,967,295], 32-bit keys
        k1  dd 255                   
        k2  dd 0ff000000h                  
        k3  dd 10000000h
                                                        
        plain_txt_msg    db "Plain text: ",0            ; some screen messages
        keys_msg         db 0dh,0ah,"Enter keys: ",0
        key_num          db "k[ ",30h," ] = ",0
        
        wait_for_key     db 0dh,0ah,0ah,"Press any key to exit the app... ",0   
        newline          db 0dh,0ah,0
    
        key128bit        db 0dh,0ah,"128-bit key: ",0
        
        table_header     db 0dh,0ah,0ah,"|Cycle|Plain Text      |Encryption      |Decryption      |",0
        table_row        db 0dh,0ah,"|     |                |                |                |",0      
        cycle_num        db "<",07h,30h,0eh,">",07h     ; char + attribute
        
        text_mode_mem    dw 0b800h                      ; text mode vodeo mem address
        pt_buffer_start  dw 0018h                       ; 0b800h:0018h, text_mode_mem + 24 bytes, write plain text here
        k_buffer_start   dw 00cah                       ; 0b800h:00cah, text_mode_mem + 202 bytes, write keys here
        trimmed_value    dw 00ddh                       ; last digit will be red color if not fit in 32 bits
    ends
        
    assume ds:datasg,cs:code
    
    segment code 
    start:
        mov ax,datasg                       ; load ds
        mov ds,ax
        
        mov ax,[text_mode_mem]              ; set es to text mode address
        mov es,ax
    
        mov ax,3                            ; text mode
        int 10h
    
        mov ah,01h                          ; hide cursor
        mov cx,2000h
        int 10h 
    
    ;------------------------- input plain txt  
        mov si, offset plain_txt_msg        ; "Plain text: "
        call print_text                 
        
        mov di, offset plain_txt_buffer     ; store plain text here
        mov bx,[pt_buffer_start]            ; address in video memory of the plain_text_buffer  
        
        call user_input                     ; take chars from a user
    
    ;------------------------- input keys
        mov si, offset keys_msg             ; "Enter keys: "
        call print_text
    
        mov bx,[k_buffer_start]             ; address in video memory of the keys_buffer    
        mov [word input_flag],2             ; change data set to digits
        
        mov cx,4                            ; read 4 keys
        mov dx,010ch                        ; cursor position
        
        enter_keys:
            mov di, offset keys_buffer      ; write keys here
            push dx                         ; save cursor position
            push bx                         ; save video memory offset of the current key 0,1,2,3
            
            mov ah,2                        ; set cursor position
            mov bh,0
            int 10h                         ; BIOS video interrupt
            
            pop bx
            push bx
            
            mov si, offset key_num          ; print key number, "k[ ",30h," ] = " 
            push si                         ; save, lodsb inside print_text changes si
            call print_text 
                    
            call user_input                 ; take ascii digits from a user         
            
            pop si                          ; restore       
        
            call key32bit                   ; convert ascii digits to 32bit and clear keys_buffer
    
            pop bx                          ; current key position
            add bx,160                      ; move to the next key
            
            mov si, offset newline          ; go to new line
            call print_text
            
            pop dx                          ; restore cursor position
            inc dh                          ; next row
            dec cx                          ; decrement loop counter
            jnz enter_keys                  ; enter next key
    
    ;-----------------------show 128 bit key
    
        mov si,offset key128bit             ; "128-bit key: "
        call print_text
        
        call print128key                    ; this proc prints full key in hex format
    
    ;----------------------------------------   
    
        call remove_0dh                 ; remove 0dh from plain_txt_buffer
        
        mov cx,4                        ; run encryption 4 times
        mov si, offset plain_txt_buffer ; offsets to buffers
        mov di, offset enc_buffer
        mov bx, offset dec_buffer
        
        cycle_enc_dec:                  ; start encryption - decryption cycle
            push bx
            mov eax, [si]               ; p1,take 4 bytes from plain_txt_buffer
            mov [p1],eax                ; p1,p2 are used inside enc and dec procs
            add si,4                    ; move to p2
            mov eax, [si]               ; p2,take 4 bytes from plain_txt_buffer
            mov [p2],eax                ;
            
            call encrypt                ; start encryption
        
            mov eax,[p1]                ; take decrypted 2 x 4 bytes and store them inside enc_buffer   
            mov [di],eax                ; p1
            add di,4                    ; move to p2
            mov eax,[p2]            
            mov [di],eax                ; p2
            
    ;-------------------------------------  
            
            call decrypt                ; start decryption
            
            pop bx                      ; pop dec_buffer address
            mov eax,[p1]                ; and save p1 and p2 inside
            mov [bx],eax                
            add bx,4
            mov eax,[p2]
            mov [bx],eax
            
            add si,4                    ; next 2 x 4 bytes of plain text
            add di,4                    ; move 8 bytes inside encryption buffer
            add bx,4                    ; move 8 bytes inside decryption buffer
            
            mov [dword sum],0           ; reset sum
            
            dec cx                      ; decrement cycles  
            jnz cycle_enc_dec           ; go to next cycle
        
    ;------ data is in memory, now show results
    
        mov si, offset table_header     ; |Cycle|Plain Text      |Encryption      |Decryption      |
        call print_text
        
        mov cx,13                       ; print 13 rows
        
        show_table:
            mov si, offset table_row
            call print_text 
            
            dec cx
            jnz show_table
    
        call print_data                 ; show all data,number of cycle, plain text, enrypted text
                                        ; and after decryption, back to original message
    
    ;------------------------- end app
    
        mov si,offset wait_for_key
        call print_text
        
        mov ah,0
        int 16h
        
        mov ax,3                ; clear screen
        int 10h
        
        mov ax,4c00h
        int 21h
    
    ;----------------------------------------   
    ;   print128key
    ;   prints full key in hex format
    
    proc print128key
        
        mov di,986                      ; screen offset, start printing at this location
        mov si, offset k0               ; offset of first key, all the keys are in memory together
                                        ; k1 = k0 + 4 bytes
                                        ; k2 = k0 + 8 bytes
                                        ; k3 = k0 + 12 bytes
    
        add si,3                        ; bytes in memory are in litlle endian format
                                        ; translate to human readable form
                                        
        mov dx,4                        ; print 4 keys
        
        one_of_the_4_parts:                             
        
            mov cx,4                    ; 4 bytes each
    
            byte_inside:
            
                mov al,[si]             ; each byte of the key
            
                call char2hex           ; print 2 hex values
            
                dec si                  ; offset of the next byte of the key
                dec cx                  ; bytes left
                jnz byte_inside
            
            add si,8                    ; move to the next part
            add di,2                    ; insert space
            dec dx                      ; parts left
            jnz one_of_the_4_parts
            
        ret
    endp print128key
    
    ;----------------------------------------   
    ;   print_data
    ;   prints result of encrypton and decryption
    
    proc print_data
        push bp
        mov bp, sp
        
        mov cx,4                ; 4 rows            
        mov di,1604             ; memory offset of the first table row
        
        mov [word bp-2],offset plain_txt_buffer
        mov [word bp-4],offset enc_buffer
        mov [word bp-6],offset dec_buffer
        sub sp,6
        
        show_rows:
            push cx     
            
            ;cycle column
            
            mov si,offset cycle_num     ; print cycle number
            mov cx,6
            rep movsb
            
            add [byte si-4],1           ; modify row number
        
            ;plain text column
        
            add di,4
            
            mov si, [bp-2]
            mov cx,8
        
            plain_text_column:
                mov al,[si]             ; char from plain_txt_buffer
                mov [es:di],al  
                add di,1
                
                mov dl,15               ; white color
                mov [es:di],dl
                add di,159
                
                call char2hex
                
                sub di,160
                
                inc si                  ; take next char
                dec cx
                jnz plain_text_column
            
            mov [bp-2],si               ; save current plain_txt_buffer offset
            
            ;encryption column
        
            add di,2
            
            mov si, [bp-4]
            mov cx,8
        
            enc_column:
                mov al,[si]             ; char from enc_buffer
                mov [es:di],al  
                add di,1
                
                mov dl,10               ; light green color
                mov [es:di],dl
                add di,159
                
                call char2hex
                
                sub di,160
                
                inc si                  ; take next char
                dec cx
                jnz enc_column
            
            mov [bp-4],si               ; save current enc_buffer offset
    
            ;decryption column
        
            add di,2
            
            mov si, [bp-6]
            mov cx,8
        
            dec_column:
                mov al,[si]             ; char from dec_buffer
                mov [es:di],al  
                add di,1
                
                mov dl,13               ; purple color
                mov [es:di],dl
                add di,159
                
                call char2hex
                
                sub di,160
                
                inc si                  ; take next char
                dec cx
                jnz dec_column
            
            mov [bp-6],si               ; save current enc_buffer offset
    
            add di,370                  ; go next row
            
            pop cx  
            dec cx
            jnz show_rows
            
        add sp,6    
        pop bp  
        ret
    endp print_data
    
    ;----------------------------------------
    ;   remove_0dh  
    ;   removes 0dh from plain text buffer
    ;   we want only plain text
      
    proc remove_0dh
    
        mov di,offset plain_txt_buffer
        
        search_for_0dh:
            cmp [byte di],0dh
            jz search_done
                
            inc di                      ; next address of the char
            jmp search_for_0dh
        
            search_done:
                mov [byte di],0         ; remove 0dh
        ret
    endp remove_0dh
    
    ;----------------------------------------   
    ;   char2hex
    ;   prints byte as sequence of 2 hex values
    
    proc char2hex
    
        ; al - char 
        push cx                         ; save previous loop's counter
        push ax                         ; char will change after shr, save it
        mov bl,2                        ; process 2 nibbles
        
        shr al,4                        ; al = xxxxxxxx, - shr,4 - 0000xxxx, higher half moved to lower half
            
        hex_2:
            cmp al,10
            jae print_letter
            
            add al,30h                  ; print_digit
            jmp to_mem
            
            print_letter:
                add al,37h      
                    
            to_mem:
                mov [es:di],al          ; move to mem
                
            add di,1                    ; move to the attribute byte
            
            mov cl,3                    ; turquoise color
            mov [es:di],cl              ; change char attribute
            inc di
            
            dec bl              
            jz done_printing
            
            pop ax      
            and al,0fh                  ; al = 0000xxxx, lower half
            jmp hex_2
            
        done_printing:
            pop cx
        ret
    endp char2hex
    
    ;----------------------------------------
    ;   convert ascii string to 32bit value
    ;   and save in var k0,k1,k2,k3
    
    proc key32bit
        push cx
        mov di, offset keys_buffer      ; ascii digit key is stored here
        
                                        ; calculate offset of k0,k1,k2,k3
        add si,3                        ; take number of the key from string key_num
        xor ah,ah                       ; ah = 0
        mov al,[byte si]                ; al = 30h,31h,32h,33h      
        push ax                         ; save
        sub al,30h                      ; al = 0,1,2,3
        shl ax,2                        ; calculat desired offset k0 = 0,k1 = 4,k2 = 8,k3 = 12 bytes
        mov bx,ax                       
    
        pop ax                          ; restore
        inc al                          ; al = 31h,32h,33h,34h          
        mov [byte si],al                ; next key number
        
        mov si,offset k0            
        add si,bx                       ; [si] here will be each key
        
        push si                         ; save address
        
        xor ch,ch                       ;
        mov cl,[chars_in_buffer]        ; number of digits in the buffer
        
        mov ebx,0                       ; after all the calculations 32bit key will be stored here
        
        cmp cl,0                        ; no digits, only 0dh
        jz end_of_data
        cmp cl,1                        ; 1 digits
        jz last_digit   
        
        
        add di,cx                       ; location of enter in the buffer
        mov [byte di],30h               ; remove 0dh    
        sub di,cx                       ; go back to start of the buffer
        
        dec cx                          ; these digits will be mul by 10, last one will be added
        mov esi,10
            
        key_in_the_buffer:          
            xor eax,eax                 ; eax = 0
            mov al,[di]                 ; [di] - ascii digit            
            sub al,'0'                  ; al = [0...9]      
            add eax,ebx
            mul esi                     ; mul by 10
            jc key_too_big_2
            mov ebx,eax
            
            inc di
            dec cx
            jnz key_in_the_buffer
            
        last_digit: 
            xor eax,eax                 ; eax = 0
            mov al,[di]                 ; [di] - ascii digit            
            sub al,'0'                  ; al = [0...9]
            add eax,ebx         
            jc key_too_big
            mov ebx,eax
            jmp end_of_data
        
        key_too_big_2:
            push di
            mov al,0ch                  ; red digit
            mov di, [trimmed_value]     
            mov [es:di],al
            pop di
            jmp last_digit
            
        key_too_big:        
            mov al,0ch                  ; red digit
            mov di, [trimmed_value]     
            mov [es:di],al
            
        end_of_data:    
            pop si      
            mov [si],ebx                ; save 32bit key to memory
    
            mov di, [trimmed_value]     ; 
            cmp [byte chars_in_buffer],0; is buffer empty?      
            jnz key_is_visible
            
            mov al,'0'                  ; '0' if user pressed only enter                
            sub di,19                   ; move to the left
            mov [es:di],al              ; show '0'
            add di,19                   ; go back, code uses last value of trimmed_value 
    
            key_is_visible:
                add di,160              ; next location of "bad" digit
                mov [trimmed_value],di
            
    ;---------------------------------------        
        mov di, offset keys_buffer      ; clear buffer
            
        mov cx,11
        mov dl,0
            
        clear_keys_buffer:
            mov [di],dl
            inc di
            dec cx
            jnz clear_keys_buffer
            
        pop cx  
        ret
    endp key32bit
    
    ;----------------------------------------   
    ;   data encryption
    
    proc encrypt
        pusha
    
        mov cx, 32
        encryptLoop:
            ; sum += delta
            mov eax, [delta]
            add [sum], eax
    
            xor eax,eax
            ; ((p2 << 4) + k0)
            mov edx, [p2]
            shl edx, 4
            add edx, [k0]
    
            ; (p2 + sum)
            mov ebx, [p2]
            add ebx, [sum]
    
            ; ((p2 << 4) + k0) ^ (p2 + sum)
            xor edx, ebx
    
            xor ebx, ebx
    
            ; ((p2 >> 5) + k1)
            mov ebx, [p2]
            shr ebx, 5
            add ebx, [k1]
            ; ((p2 << 4) + k0) ^ (p2 + sum) ^ ((p2 >> 5) + k1)
            xor edx, ebx
            
            ;---
            add [p1],edx
            
            ;add [pe1], edx
    
            ; ((p1 << 4) + k2)
            mov edx, [p1]
            shl edx, 4
            add edx, [k2]
    
            ; (p1 + sum)
            mov ebx, [p1]
            add ebx, [sum]
    
            ; ((p0 << 4) + k2) ^ (p0 + sum)
            xor edx, ebx
    
            xor ebx, ebx
    
            ; ((p1 >> 5 ) + k3)
            mov ebx,[p1]
            shr ebx, 5
            add ebx, [k3] 
            ; ((p0 << 4) + k2) ^ (p0 + sum) ^ ((p1 >> 5 ) + k3)
            xor edx, ebx
    
            add [p2],edx
    
            ;add [pe2], edx
        loop encryptLoop
    
        popa
        ret
    endp encrypt
    
    ;----------------------------------------   
    ;   data dencryption
    
    proc decrypt
       pusha
    
        mov cx, 32
        decryptLoop:
           
            ; ((p1 << 4) + k2)
            mov edx, [p1]
            shl edx, 4
            add edx, [k2]
    
            ; (p1 + sum)
            mov ebx, [p1]
            add ebx, [sum]
            
            ; ((p1 << 4) + k2) ^ (p1 + sum)
            xor edx, ebx
    
            xor ebx, ebx
    
            ; ((p1 >> 5) + k3)
            mov ebx, [p1]
            shr ebx, 5
            add ebx, [k3]
            ; ((p1 << 4) + k0) ^ (p2 + sum) ^ ((p1 >> 5) + k3)
            xor edx, ebx
            
            sub [p2], edx   
        
        
            ; ((p2 << 4) + k0)
            mov edx, [p2]
            shl edx, 4
            add edx, [k0]
    
            ; (p2 + sum)
            mov ebx, [p2]
            add ebx, [sum]
    
            ; ((p2 << 4) + k0) ^ (p2 + sum)
            xor edx, ebx
    
            xor ebx, ebx
    
            ; ((p2 >> 5 ) + k1)
            mov ebx,[p2]
            shr ebx, 5
            add ebx, [k1] 
            ; ((p2 << 4) + k0) ^ (p2 + sum) ^ ((p2 >> 5 ) + k1)
            xor edx, ebx
    
            sub [p1], edx
    
            ; sum -= delta
            mov eax, [delta]
            sub [sum], eax
        loop decryptLoop
    
        popa
        ret
    endp decrypt
    
    ;----------------------------------------
    ;   data input, buffer operations
    ;   di - offset to buffer
    
    proc user_input
        
        mov dh,0                        ; chars counter
        mov dl,0                        ; flag  
        mov si,[input_flag]             ; switch between data sets
                                        ; if keys we need only digits
                                        ; if plain text we want more chars
        new_char:
            mov ah,0                    ; wait for key
            int 16h                     ; BIOS keyboard interrupt
            
    ;----------------------------------------
            call check_chars            ; we want chars from set 1 or set 2
    
            ; check flag
    
            cmp dl,0                    ; 0 - data set 1 - keys (20h - 7fh) 
            jz char_ok                  ; or  data set 2 - keys (30h - 39h) 
                                
            cmp dl,1                    ; 1 - wrong char, do nothing
            jz reset_flag       
    
            cmp dl,2                    ; 2 - backspace, delete char
            jz key_backspace    
    
            cmp dl,3                    ; 3 - enter, accept string
            jz insert_0dh       
    
    ;----------------------------------------       
            char_ok:
                mov [di],al             ; write to buffer
                inc di                  ; next position in buffer
    
                mov [es:bx],al          ; write to video mem
                add bx,2                ; next offset in video mem
            
                inc dh                  ; count chars               
                
                cmp si,1                ; switch between data sets
                jz plain_txt_ds
                
                cmp dh,[k_buffer_size]  ; is keys_bufferr full?
                jz insert_0dh           ; yes, we are done here
                jmp new_char            ; no, enter next char
                
                plain_txt_ds:
                    cmp dh,[pt_buffer_size] ; is plain_text_buffer full?
                    jz insert_0dh           ; yes, we are done here
                    jmp new_char            ; no, enter next char
                
    ;----------------------------------------
            key_backspace:      
                cmp dh,0                ; is buffer empty?
                jz reset_flag           ; yes
                                        ; no
                dec di                  ; move buffer pointer to the left
                dec dh                  ; decrease number of the chars in the buffer 
                mov al,0                ; 
                mov [di],al             ; delete char in buffer
                
                sub bx,2                ; and clear char on the screen too
                mov al,0                ; 
                mov [byte es:bx],0      ; remove char from video mem
                
                reset_flag:
                    mov dl,0
                
                    jmp new_char        
    
            insert_0dh:
                mov al,0dh              ; insert enter 
                mov [di],al             ; at the end of the string                  
            
                mov [chars_in_buffer],dh; used in key32bit proc
        ret
    endp user_input
    
    ;----------------------------------------
    ;   check_chars
    ;   proc accepts only chars from data set 1 and 2
    
    proc check_chars
        
        enter_key:
            cmp al,0dh                  ; enter
            mov dl,3                    ; flag = 2, accept entered data
            jz checking_done    
    
        backspace:
            cmp al,08h                  ; backspace
            mov dl,2                    ; flag = 3, delete previous char
            jz checking_done
    
        cmp si, 1                       ; plain text data set
        jz plain_txt_data_set
        
        from_30h:                       ; keys data set
            cmp al,30h                  ; 0
            jb wrong_char
            
        to_39h:
            cmp al,39h                  ; 9
            ja wrong_char
            mov dl,0                    ; flag = 0, all good
            jmp checking_done       
        
        plain_txt_data_set:
            from_20h:   
                cmp al,20h              ; space
                jb wrong_char
            
            to_7eh:
                cmp al,7eh              ; tilde
                ja wrong_char           
                mov dl,0                ; flag = 0, all good
                jmp checking_done   
            
        wrong_char:
            mov dl,1                    ; flag = 1, wrong char  
            
        checking_done:      
            ret
    endp check_chars
    
    ;----------------------------------------
    ; print string
    
    proc print_text
    
        next_letter:
            mov ah,0eh
            lodsb       ; al = ds:[si]
            or al,al
            jz done
            
            int 10h
            jmp next_letter
        
        done:
        ret
    endp print_text
    ends
    end start