0

I'm using the MASM syntax with Irvine32 Library on Visual Studio.

I've attempted to make this program convert binary to decimal and vise versa, and now I'm stuck on how to convert binary to hexa and vise versa

Here's my code

INCLUDE Irvine32.inc



.data
    str1 BYTE ">>> Please select the conversion type: ", 0
    str2 BYTE " 1. Binary to Decimal", 0
    str3 BYTE " 2. Decimal to Binary", 0
    str4 BYTE " 3. Exit", 0 
    str5 BYTE "-----------------------------------------", 0
    str6 BYTE "Enter your choice: ", 0
    str7 BYTE "Please Enter 8-bit binary digits (e.g., 11110000): ", 0
    str8 BYTE "The decimel integer of ", 0
    str9 BYTE " is ", 0
    str10 BYTE " ", 0
    str11 BYTE "Please Enter a decimal integer less than 256: ", 0
    str12 BYTE "The binary of ", 0
    str13 BYTE " is ", 0
    str14 BYTE " ", 0
    str15 BYTE "Bye.", 0
    choice DWORD ?
    num1 BYTE 9 DUP(? )
    num2 WORD ?
    numLength DWORD 0
    outLoop DWORD 0
    base DWORD 2
    deciNum DWORD ?

.code
main PROC
    
    _mainprog :             ;USED TO DISPLAY THE MENU & EXIT
    mov edx, offset str1 
    call WriteString
    call crlf
    mov edx, offset str2
    call WriteString
    call crlf
    mov edx, offset str3
    call WriteString
    call crlf
    mov edx, offset str4
    call WriteString
    call crlf
    mov edx, offset str5
    call WriteString
    call crlf
    mov edx, offset str6
    call WriteString
    call ReadDec            ;DISPLAY OUTPUT
    mov choice, eax         ;EAX = 1
    cmp choice, 1           ;IF = 1
    je _proc1               ;JUMP TO PROC1
    cmp choice, 2           ;IF = 2
    je _proc2               ;JUMP TO PROC2
    cmp choice, 3           ;IF = 3
    je _proc3               ;JUMP TO PROC3

    _proc1 :                ;BINARY TO DECIMAL
    mov edx, offset str7    
    call WriteString
    mov edx, offset num1    ;ASSIGN NUM1 TO EDX TO WRITE THE BINARY IN STRING FORMAT
    mov ecx, sizeof num1    ;ASSIGN TO ECX TO ACT AS THE COUNTER (COUNT THE LENGTH OF THE BINARY DIGIT)
    call ReadString         ;USER INPUT BINARY NUMBER
    mov numLength, eax      ;USER INPUT EAX STORED IN NUMLENGTH
    mov eax, 0              ;EAX ASSIGNED TO 8
    mov esi, 0              ;ESI TO ACCESS THE STRING BINARY INPUT THAT WE ENTERED
    mov ecx, numLength      
    whileProc1 :
    cmp ecx, 0              ;COMPARE COUNTER WITH 0
    je printResult          ;IF ECX =/ 0, IT WILL NOT JUMP TO PRINT RESULT
    mov outLoop, ecx        ;8 ASSIGNED TO OUTLOOP AND ASSIGNED TO REGISTER ECX
    ifProc :                
    cmp num1[esi], '0'      ;IF 1ST VALUE IN THE BINARY DIGIT = 0
    je incEsiProc           ;JUMP TO INCREASE ESI PROC
    elseIfProc:
    cmp num1[esi], '1'      ;IF 1ST VALUE IN BINARY DIGIT = 1
    mov ecx, numLength      ;NUMLENGTH ASSIGNED TO ECX
    sub ecx, esi            ;
    dec ecx
    mov eax, 1
    whileProc2 :
    je stopProc
    mov ebx, base
    mul ebx
    dec ecx
    jmp whileProc2
    stopProc :
    add deciNum, eax
    jmp incEsiProc
    incEsiProc :
    inc esi
    mov ecx, outLoop
    dec ecx
    jmp whileProc1
    printResult :
    mov edx, offset str8
    call WriteString
    mov edx, offset num1
    call WriteString
    mov edx, offset str9
    call WriteString
    mov eax, deciNum
    call WriteDec
    mov edx, offset str10
    call WriteString
    call crlf
    call crlf
    jmp _mainprog

    _proc2:                 ;DECIMAL TO BINARY
    mov edx, offset str11
    call WriteString
    mov edx, offset num2
    call ReadInt
    mov num2, ax
    mov ebx, 1
    mov edx, offset str12
    call WriteString
    mov ax, num2
    call WriteDec
    mov edx, offset str13
    call WriteString
    call WriteBinB
    mov edx, offset str14
    call WriteString
    call crlf
    call crlf
    jmp _mainprog

    _proc3:
    mov edx, offset str15
    call WriteString
    call crlf
    call WaitMsg
    exit
main ENDP
END main


Able to ask user for input a number and convert hexa to binary/decimal and vise versa

  • Input binary number on command line as in `_proc1`. This value is stored in `deciNum` and then use `mov eax, deciNum`, `call WriteHex`. Should work I think. :) – Nassau Jun 09 '23 at 07:27
  • In this library You have procedures `ReadHex`, `WriteBin`, `WriteDec`. – Nassau Jun 09 '23 at 07:36
  • @0Signal Ive added as such: '_proc4: ; BINARY TO HEXA mov edx, offset str8 call WriteString mov edx, offset num1 mov ecx, sizeof num1 call ReadString mov eax, deciNum call WriteHex call crlf call crlf jmp _mainprog ' But the output is 00000000 – Arciel Shin Jun 09 '23 at 08:02
  • Copy `_proc1` code and change `WriteDec` to `WriteHex` – Nassau Jun 09 '23 at 08:09
  • I got it to work! How do I convert Hexa to Binary/Decimal? – Arciel Shin Jun 09 '23 at 08:31
  • Use `call ReadHex`. Input hex value, result will be stored in eax and use `WriteBin` or `WriteDec` – Nassau Jun 09 '23 at 08:42
  • It works now! I have a problem now where if I run operations continuosly, the values somehow add up? Eg. If I choose binary to decimal, input 11001100, the result is 204. Then I choose binary to decimal again, and input 11001100 again, the result is now 408? Is there a way to fix this? – Arciel Shin Jun 09 '23 at 08:56
  • Yeah, I noticed. Just reset deciNum` mov [deciNum],0` before calculations. Add this after each label `_procX`, see code below. – Nassau Jun 09 '23 at 09:45
  • For numbers that fit in a 32-bit register, see [How to convert a binary integer number to a hex string?](https://stackoverflow.com/q/53823756) . (In that question, "binary" is talking about a binary integer in a register, not a string of ASCII '0' / '1' base-2 digits. Computers are naturally binary, storing numbers as a group of bits.) – Peter Cordes Jun 09 '23 at 14:50
  • See also [How do I print an integer in Assembly Level Programming without printf from the c library? (itoa, integer to decimal ASCII string)](https://stackoverflow.com/a/46301894) / [NASM Assembly convert input to integer?](https://stackoverflow.com/a/49548057) re: handling bases (like 10) that aren't powers of 2 the way base 2 and base 16 are. Of course, all those Q&As are doing it fully manually, not calling library functions like WriteDec or WriteHex which do all the work for you. – Peter Cordes Jun 09 '23 at 14:51

1 Answers1

1

This is how program looks now, I used this algorithm for conversion binary string to integer. :)

  1. Binary to Decimal
  2. Decimal to Binary
  3. Binary to Hex
  4. Hex to Binary
include \masm32\include\Irvine32.inc
includelib \masm32\lib\Irvine32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

.data
    menu             BYTE "-----------------------------------------",13,10         
                     BYTE "    Please select the conversion type ",13,10 
                     BYTE "-----------------------------------------",13,10
                     BYTE " 1. Binary to Decimal",13,10
                     BYTE " 2. Decimal to Binary",13,10
                     BYTE " 3. Binary to Hex",13,10
                     BYTE " 4. Hex to Binary",13,10
                     BYTE "-----------------------------------------",13,10                     
                     BYTE " 5. Exit",13,10
                     BYTE "-----------------------------------------",13,10
                     BYTE "Enter your choice: ",0
    menu_error       BYTE "*** You entered wrong number. Try again.", 0    
   
    bin_input_msg    BYTE "Please enter 8-bit binary digits (e.g., 11110000): ", 0
    bin_string_error BYTE "*** Cannot convert binary string to integer. Try again.", 0 

    bin_msg_1        BYTE "The binary of ", 0

    dec_input_msg    BYTE "Please enter a decimal integer less than 256: ", 0
    dec_input_error  BYTE "*** Enter value from range 0 - 255.", 0

    dec_msg_1        BYTE "The decimal integer of ", 0    

    hex_input_msg    BYTE "Please enter a hex value: ", 0
  
    hex_msg_1        BYTE "The hex of ", 0

    is               BYTE " is ", 0    
    exit_msg         BYTE "Bye.", 0
    
    binary_string    BYTE 9 DUP(0)
 
    InitFlag         DB 0
    consoleInHandle  DWORD ?        ; handle to console input device
    consoleOutHandle DWORD ? 
;----------------------------------------------------------------------   
    CheckInit MACRO

    ; Helper macro
    ; Check to see if the console handles have been initialized
    ; If not, initialize them now.

   LOCAL exit
      cmp InitFlag,0
      jne exit
      call Initialize
     exit:
   ENDM   
     
;----------------------------------------------------------------------
.code
main PROC

;----------------------------------------------------------------------
;   MENU    
    
    _mainprog: 
        call Clrscr
                
        mov edx, offset menu
        call WriteString        
        call ReadDec            ;eax = user's choice       
        call crlf           
        
        cmp eax, 1           
        je _bin_dec   
        cmp eax, 2           
        je _dec_bin             
        cmp eax, 3           
        je _bin_hex   
        cmp eax, 4           
        je _hex_bin             
        cmp eax, 5           
        je _exit                
        jmp _wrong_number
        
;----------------------------------------------------------------------
;   BINARY TO DECIMAL

    _bin_dec:
        call BinaryString2Int                 
        mov edx, offset dec_msg_1
        call WriteString
        mov edx, offset binary_string 
        call WriteString
        mov edx, offset is
        call WriteString
        call WriteDec
        call _new_line       

        jmp _mainprog

;----------------------------------------------------------------------
;   DECIMAL TO BINARY

    _dec_bin:                 
        mov edx, offset dec_input_msg
        call WriteString
        call ReadDec
        cmp eax,255             ;check if value is 0 - 255 
        ja GreaterThan255
        
        mov edx, offset bin_msg_1
        call WriteString
        call WriteDec
        mov edx, offset is
        call WriteString
        mov ebx, 1             ; 1 - byte, 2 - word, 4 - dbl word
        call WriteBinB
        call _new_line 
              
        jmp _mainprog
        
        GreaterThan255:
            call crlf 
            mov edx, offset dec_input_error
            call WriteString
            call _new_line       
   
            jmp _dec_bin

;----------------------------------------------------------------------
;   BINARY TO HEX

    _bin_hex:                
        call BinaryString2Int  
        mov edx, offset hex_msg_1
        call WriteString
        mov edx, offset binary_string
        call WriteString
        mov edx, offset is
        call WriteString
        call WriteHex
        call _new_line       
        
        jmp _mainprog

;----------------------------------------------------------------------
;   HEX TO BINARY

    _hex_bin:                 
        mov edx, offset hex_input_msg
        call WriteString
        call ReadHex        
        mov edx, offset bin_msg_1
        call WriteString       
        call WriteHex
        mov edx, offset is
        call WriteString
        mov ebx,4              ; 1 - byte, 2 - word, 4 - dbl word
        call WriteBinB
        call _new_line
        
        jmp _mainprog
        
;----------------------------------------------------------------------
;   NUMBER IS NOT ON THE MENU LIST

    _wrong_number:      
        mov edx, offset menu_error
        call WriteString
        call _new_line

        jmp _mainprog

;----------------------------------------------------------------------
;   NEW LINE

    _new_line:
        call crlf
        call crlf
        call WaitMsg            
        call crlf        
        ret
        
;----------------------------------------------------------------------
;   EXIT

    _exit:
        mov edx, offset exit_msg
        call WriteString
        call crlf
        call WaitMsg
        exit
        
main ENDP

BinaryString2Int Proc

;----------------------------------------------------------------------
;   INPUT - BINARY STRING

    _bin_string_input:
        mov edx, offset bin_input_msg    
        call WriteString
        mov edx, offset binary_string    
        mov ecx, sizeof binary_string     
        call ReadString  

;----------------------------------------------------------------------
;   BINARY STRING TO INT
;   Algorithm taken from -> https://stackoverflow.com/a/49548057/20889367

    mov edi, offset binary_string
    movzx   eax, byte ptr [edi]    ; start with the first digit
    sub     eax, '0'               ; convert from ASCII to number
    cmp     al, 1                  ; check that it's a decimal digit [0..1]
    jbe     _loop_entry            ; too low -> wraps to high value, fails unsigned compare check

    xor     eax,eax                ; else: bad first digit: return 0
    jmp conversionError ;ret

    ; rotate the loop so we can put the JCC at the bottom where it belongs
    ; but still check the digit before messing up our total

    _next_digit:                  
        ; lea     eax, [eax*4 + eax]   
        lea     eax, [eax*2 + ecx]    ; total = total*2 + digit
        ; imul eax, 2  / add eax, ecx

    _loop_entry:
        inc     edi
        movzx   ecx, byte ptr[edi]
        sub     ecx, '0'
        cmp     ecx, 1
        jbe     _next_digit  
        ret                            ; return with total in eax 

        conversionError:
            call crlf 
            mov edx, offset bin_string_error
            call WriteString
            call crlf  
            call crlf        
            jmp _bin_string_input
    ret
BinaryString2Int Endp

Initialize PROC private
;----------------------------------------------------------------------
;   Get the standard console handles for input and output,
;   and set a flag indicating that it has been done.
;   Updated 03/17/2003

    pushad

    INVOKE GetStdHandle, STD_INPUT_HANDLE
    mov [consoleInHandle],eax

    INVOKE GetStdHandle, STD_OUTPUT_HANDLE
    mov [consoleOutHandle],eax

    mov InitFlag,1

    popad
    ret
Initialize ENDP
END main
Nassau
  • 377
  • 1
  • 1
  • 8
  • This helps a lot! :D I understand now. How do I convert Hexa to Binary/Decimal though? – Arciel Shin Jun 09 '23 at 08:42
  • 2
    This duplicates the code for base-2 string to integer. If you factor that out into a function the returns a 32-bit integer in a register, you only need one copy of that code, @ArcielShin. (Also, it's way over-complicated, apparently using `mul` in a loop for every bit, instead of a shift. If you're going to use the same code for every base, don't duplicate it. Also, as shown in [other answers](//stackoverflow.com/a/49548057), there's a better algorithm that only needs one multiply per input digit, not a loop. At least use `imul eax, base`, not `mul` since you don't want mul's EDX output.) – Peter Cordes Jun 09 '23 at 15:38
  • Well, I changed few things. It is better now? :) – Nassau Jun 12 '23 at 15:03
  • 1
    Less code duplication, but now it's spaghetti code. If binary to decimal and binary to hex both just called a function, that would be easier to follow. Instead, they just *jump* to the same place, and then further branching on `choice` happens later, so the logic based on `choice` is scattered around when it doesn't need to be. Also, a block of about a dozen instructions involving a call to `WriteBinB` is still duplicated in two places. – Peter Cordes Jun 12 '23 at 18:10
  • And there's mostly-unused global variables like `numLength` that get set but not read, and redundant comments like `mov numLength, eax ;USER INPUT EAX STORED IN NUMLENGTH`. We can already see the `mov`, and the previous line was a function call so we expect it returned something in EAX. The variable name makes it pretty obvious it was a string length. Same in your chain of `cmp`/`je`, the comments don't need to reiterate that. (Also, you still have `choice` in EAX, compare with that.) I guess that was adapted from the OP's bad code, where they didn't even have meaningful names. – Peter Cordes Jun 12 '23 at 18:13
  • Anyway, think about how you might write this in C: you'd want some functions like `read_bin` / dec / bin, and `write_dec` / hex / bin. A menu choice would jump to code that called two of those functions, one for input, one for output. The trick is to find a good way to report errors if you want to bail out and not continue to print, though, so maybe a 2nd return value since this is asm where we can easily do that. e.g. in EDX, or in a FLAGS condition. – Peter Cordes Jun 12 '23 at 18:16
  • Meaningful names for the strings is a very good change. Making one multi-line string instead of using a block of multiple `WriteString` calls would make the menu code much more compact, for cases where a set of lines are always printed together. Like the stuff at the top of `mainprog` would be just be `call Clrscr` / `mov edx, offset whole_menu`/`call WriteString`. IDK if that's considered bad style for Irvine32 programs, but it seems insane to me to make 10 I/O calls when one would be just as easy. Of course, IDK how much time you care to spend tweaking someone else's homework assignment! – Peter Cordes Jun 13 '23 at 17:33