1

so I have a program that converts two digit hexadecimal number to decimal number. I need to make it so you can enter digits from 0...9 and letter a...f and A...F as for now it converts lowercase letters to uppercase with line " SUB DL,32H " but it also changes the value of uppercase letters. How could it be fixed and the input restricted to only a...f, A...F and 0...9?

#MAKE_EXE#

DSEG    SEGMENT 'DATA'  

MSG DB 'Enter a two-digit hexadecimal number: $'

DSEG    ENDS

SSEG    SEGMENT STACK   'STACK'
    DW      100h    DUP(?)  
SSEG    ENDS

CSEG    SEGMENT 'CODE'  

;*******************************************

START   PROC    FAR 

PUSH    DS
MOV     AX, 0
PUSH    AX

MOV     AX, DSEG
MOV     DS, AX  

MOV AH,09h      
MOV DX, OFFSET MSG
INT 21h
XOR AX,AX       
MOV AH,1H       
INT 21H 
MOV DL,AL   
SUB DL,30H  
CMP DL,9H   
JLE M1  
SUB DL,7H   
SUB DL,32H
M1:     
MOV CL,4H   
SHL DL,CL   
INT 21H 
SUB AL,30H  
CMP AL,9H   
JLE M2  
SUB AL,7H   
M2:     
ADD DL,AL   

        
RET     
 
START   ENDP    

;*******************************************

CSEG    ENDS    

    END    START  
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Jim978
  • 11
  • 1
  • 4
  • Assuming ASCII encoding, characters are just a single byte each. The oridinal range of uppercase A-Z is [65-90]. All you need to do is check to see if the current character is in that range, and if so then don't modify the value. – h0r53 Nov 29 '21 at 16:21
  • This really isn't an assembly language problem or question; you're asking about character codes, which are the same in language like C. – Erik Eidt Nov 29 '21 at 17:27
  • [What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa?](https://stackoverflow.com/a/54585515) shows how to detect alphabetic characters (and in the process force to upper or lower case). – Peter Cordes Nov 30 '21 at 03:54

1 Answers1

1

as for now it converts lowercase letters to uppercase with line " SUB DL,32H "

That's not what this instruction does! The conversion from lowercase to uppercase would have to subtract 32 decimal, same as 20h hexadecimal.

How could it be fixed and the input restricted to only a...f, A...F and 0...9?

It's important to know what the ASCII codes are for these characters. It will allow us to write an efficient code with few comparisons.

char   ASCII
0..9   30h..39h
A..Z   41h..5Ah
a..z   61h..7Ah

Next code validates the user input and delivers in AL a value in the range 0..15. Since your program needs this procedure twice, you should put this code in a subroutine that your main code call's twice.

GetChar:
 mov  ah, 01h       
 int  21h
 sub  al, 30h
 cmp  al, 9
 jbe  M1               ; It was a digit 0..9

 sub  al, 41h - 30h    ; -30h because that has already been subtracted!
 cmp  al, 5
 jbe  M2               ; It was an uppercase A..F

 sub  al, 61h - 41h    ; -41h because that has already been subtracted!
 cmp  al, 5
 ja   GetChar          ; It's not lowercase a..f, invalid hexdigit, redo

M2:
 add  al, 10           ; 0..5 + 10 -> 10..15
M1:

Please note that I used the unsigned jbe instead of the signed jle.

CMP DL,9H
JLE M1

The ASCII codes range from 0..255. If you use the signed jle here then an additional 128 characters will get treated as digits!


Shorter code

Still validating the user input, but especially removing that 3rd branch can make it faster.

GetChar:
 mov  ah, 01h       
 int  21h
 sub  al, 30h
 cmp  al, 9
 jbe  M1               ; It was a digit 0..9

 or   al, 20h          ; Case insensitiveness
 sub  al, 61h - 30h    ; -30h because that has already been subtracted!
 cmp  al, 5
 ja   GetChar          ; It's not A..F a..f, invalid hexdigit, redo
 add  al, 10           ; 0..5 + 10 -> 10..15

M1:

Shortest code

Only if you trust that the user at the keyboard will always input a valid hexdigit. Not very realistic!

GetChar:
 mov  ah, 01h       
 int  21h
 sub  al, 30h
 cmp  al, 9
 jbe  M1               ; It was a digit 0..9

 and  al, 15
 add  al, 9            ; 1..6 + 9 -> 10..15

M1:
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • Since they ultimately want hex-digit -> integer I think, it would be just as easy to `and al, 0xf` after distinguishing between alphabetic or decimal digits. (add 9 since `'a' & 0xf = 1`, or add nothing). You can skip actually creating an upper-case or lower-case ASCII code. – Peter Cordes Nov 30 '21 at 03:56
  • @PeterCordes Wouldn't just ANDing have to assume that the input was indeed a valid hexdigit? – Sep Roland Dec 01 '21 at 17:58
  • Yes. But it looks like the code in the question isn't doing input validation either. You still get *a* number; maybe that's a feature if you can enter numbers up to 24 as a single character this way :P Otherwise, you can just do a case-insensitive check for being an alphabetic character from a..f instead of the normal a..z as in [this answer](https://stackoverflow.com/questions/54536362/what-is-the-idea-behind-32-that-converts-lowercase-letters-to-upper-and-vice/54585515#54585515)... Oh good, you already thought of that. :) – Peter Cordes Dec 01 '21 at 23:12
  • 1
    If we don't validate *at all*, we can even go branchless: `9*(c>>6) + (c&0xf)` is still only a few instructions if we can use 386 LEA to multiply by 9 cheaply (not `imul`). https://godbolt.org/z/KKz36fv4s Similar cost to using CMOV to select between `c&0xf` and `(c&0xf) + 9` based on `test c, 1<<6` (included at end of Godbolt link.) – Peter Cordes Dec 01 '21 at 23:28