1

I am trying to make a program where the user have to enter a string and get an reversed output. Moreover, it should change all lowercase letters to uppercase and uppercase to lowercase. I've already done program where you can enter 1 character. My next goal is to get as many characters as I want.

I did some research and came up with this code:

org 100h
include emu8086.inc
.DATA
  STR1 DB 0DH,0AH, 'Input: $'
  STR2 DB 0DH,0AH, 'Output: $'
  nl db 0dh,0ah,'$' 

.CODE
  START:
    MOV AX, @DATA
    MOV DS, AX
    cmp al, 0x41h
    JGE IsInLowerCaseRange



  Disp:
    LEA DX,STR1
    MOV AH,09H
    INT 21H

    MOV CL,00
    MOV AH,01H

  Read:
    INT 21H

    MOV BL,AL

    PUSH BX
    inc cx
    CMP AL,0DH

    JZ DISPLAY
    JMP READ

  Display:
    LEA DX,STR2
    MOV AH,09H
    INT 21H

    lea dx, nl
    mov ah,09h
    int 21h

  ANS:
    MOV AH,02H
    POP BX
    MOV DL,BL
    INT 21H
    LOOP ANS

  IsInLowerCaseRange:
    cmp al, 0x5Ah
    jle DisplayLowerCaseLetter
    cmp al, 0x61h
    jge IsInUpperCaseRange
    jmp NotALetter


  DisplayLowerCaseLetter:
    add al, 0x20h
    mov ah, 0xEh
    int 10h
    jmp exit


  IsInUpperCaseRange:
    cmp al, 0x7Ah
    jle DisplayUpperCaseLetter
    jmp NotALetter


  DisplayUpperCaseLetter:
    sub al, 0x20h
    mov ah, 0xEh
    int 10h
    jmp exit 


  NotALetter:
    printn
    print "The input character is not a letter."
    exit:
    hlt 

.EXIT
END  START

Now I am getting a wrong output. For example if you enter Hello it will return olleHh. I am completely confused since I can't figure out my error. Also, I am new in Assembly. The output that I expect is OLLEh.

Working Code:

org 100h
include emu8086.inc


.DATA
   STR1 DB 0DH, 0AH, 'Input: $'
   STR2 DB 0DH, 0AH, 'Output: $'
   Nl DB 0Dh, 0Ah,'$' 


.CODE
START:
    MOV AX, @DATA
    MOV DS, AX


DISP:
    LEA DX,STR1
    MOV AH,09H
    INT 21H
    MOV CL,00
    MOV AH,01H


READ:
    INT 21H
    MOV BL, AL
    PUSH BX
    INC CX
    CMP AL, 0DH
    JZ DISPLAY
    CMP AL, 'A'                 ; < then A  
    JB  NotALetter
    CMP AL, 'Z'                 ; > then Z 
    JA  AGAIN                   ; repeat again
    JMP CONTINUE1


AGAIN:  
    CMP AL, 'a'                 ; < then a
    JB  NotALetter  
    CMP AL, 'z'                 ; > then z 
    JA  NotALetter       


CONTINUE1:
    JMP READ


DISPLAY:
    LEA DX, STR2
    MOV AH, 09h
    INT 21H
    LEA DX, NL
    MOV AH, 09h
    INT 21h
    POP BX                      ; pop enter key


ANS:
    MOV AH, 02h
    POP BX                      ; pop the character 
    CMP BL, 'a'                 ; check if its in upper case
    JB  toLower                 ; if yes then jmp to toLower 
    SUB BL, 32                  ; if not in upper case then convert to upper case
    JMP CONTINUE2


toLower:
    ADD BL, 32                  ; convert to lower case
    ; Probably have to subtract 32 if BL = 20h


CONTINUE2:
    MOV DL, BL
    INT 21H
    LOOP ANS  
    JMP EXIT                    ; if everything is fine jmp to exit                 


NotALetter:        
    printn
    print "The input character is not a letter."    


EXIT:
    hlt 

.EXIT
END  START
John E.
  • 321
  • 4
  • 11
  • 2
    Use your simulator to step through the code. The error should be obvious. Hint: what happens in the `ANS` loop? PS: comment your code especially if you want others to understand it (it also helps you find your own mistakes). – Jester Dec 08 '17 at 01:36
  • @Jester Thank you very much. Will follow your advice! – John E. Dec 08 '17 at 01:37
  • @Jester I am still trying to figure out my mistake. As long as I understand, I have to change characters in my `ANS`. Can you please clarify it a little bit more? I really want to understand my problem. – John E. Dec 08 '17 at 02:01
  • 1
    related: [case flip an ASCII string (skipping non-alphabetic characters)](https://stackoverflow.com/questions/35932273/how-to-access-a-char-array-and-change-lower-case-letters-to-upper-case-and-vice/35936844#35936844). Use that logic inside your reverse loop. – Peter Cordes Dec 08 '17 at 02:48
  • 2
    At the very `START:` you have non-sense `cmp al, 0x41h` `JGE IsInLowerCaseRange` without any comment, what it should do. It shows that you don't understand what you have in source at all, but also without your comments (showing your human intent) it's impossible to see why you don't understand it, and what is your actual mistake. Did you just forgot to load `al`? Do you believe the `mov ax,@DATA` is magic, which loads your `al` with character value? etc... Use emu8086 built-in debugger to step over single instructions slowly, and reason about each of them, why it is there & what it does. – Ped7g Dec 08 '17 at 11:57
  • 1
    Just running your code and receiving correct output on screen is definitely not enough to validate your code. You can get correct output many times by sheer luck and accident, even when the code contains fundamental problems, and would break on almost every other input change. Meticulously debugging each instruction added to code, and reasoning about it, is the only way how to validate assembly code properly. – Ped7g Dec 08 '17 at 11:59

1 Answers1

1

You are making it complex.

First you display all the letters of your string in reverse order with the following code:

ANS:
MOV AH,02H
POP BX
MOV DL,BL
INT 21H
LOOP ANS

and then you try to flip the cases with the following code:

IsInLowerCaseRange:
   cmp al, 0x5Ah
   jle DisplayLowerCaseLetter
   cmp al, 0x61h
   jge IsInUpperCaseRange
   jmp NotALetter  

DisplayLowerCaseLetter:
    add al, 0x20h
    mov ah, 0xEh
    int 10h
    jmp exit


  IsInUpperCaseRange:
    cmp al, 0x7Ah
    jle DisplayUpperCaseLetter
    jmp NotALetter


  DisplayUpperCaseLetter:
    sub al, 0x20h
    mov ah, 0xEh
    int 10h 

Problem is with using 0EH service of INT 10H

Why do you get that extra h letter at the end of your output string ?

You are using INT 10h which is used for video services and the service 0EH of INT 10h writes the specified character in AL to the current cursor position. Since the last character in AL was capital H in your example and which was flipped to lower case h by your code. That is where you get that extra h at the end of your output string.

Why don't you get all the letters flipped ?

Since the service 0EH of INT 10h writes only one character to the screen (not the string) you get only H to be flipped.

Solution:

You could have simply flipped the cases and displayed the reverse string in one go and that without using INT 10h.

Here is a simple code which is well documented and that does the job:

 org 100h
.DATA

  str1 db 10,13, 'Input: $'
  str2 db 10,13, 'Output: $'    
  errMsg db 10,13, 'The input character is not a letter.$'  

.CODE

  START:    

    mov ax, @DATA
    mov ds, ax

    mov dx, offset str1
    mov ah, 09h
    int 21h

    mov cl,00
    mov ah,01h 

    mov bx, '#'   ; store some random character 
                  ; into stack to remeber when to stop
    push bx       ; poping the character while diplaying

  Read: 

    int 21h

    cmp al, 13    ; check if enter key is pressed

    je DISPLAY    ; if yes then display the letters if any

    cmp al, 'A'     ; check if ASCII value of inputted charater is less than ASCII value of capital A  
    jb  NotALetter  ; if yes then print "no a letter"

    cmp al, 'Z'     ; check if ASCII value of inputted charater is not greater than ASCII value of capital Z 
    jna  letterFound  ; if not then continue 

    cmp al, 'a'     ; check if ASCII value of inputted character is less than ASCII value of small a 
    jb  NotALetter  ; if yes then print "no a letter"

    cmp al, 'z'     ; check if ASCII value of inputted character is greater than ASCII value of small z 
    ja  NotALetter  ; if not then continue 

    letterFound:

    mov bl, al

    push bx         ; store the letter in stack

    jmp READ

  Display:

    mov dx,offset str2
    mov ah,09h
    int 21h

  ANS:

    pop bx

    cmp bl, '#'   ; check if no more letter are available in stack
    je exit       

    mov ah, 02h
    cmp bl, 'a'   ; check if the letter is in upper case
    jb  toLower   ; if yes then jmp to toLower 

    sub bl, 32    ; if not in upper case then convert to upper case
    jmp continue

  toLower:

    add bl, 32  ; convert to lower case

  continue:

    mov dl, bl
    int 21h
    jmp ans

  NotALetter: 

    mov ah, 09h
    mov dx, offset errMsg  ; display error message
    int 21h

  exit:

    mov ah, 04ch
    int 21h

END  START

And as @Ped7g said you should use comment in your program because that explain what you are trying to do in your program and makes it easy for people to debug it.

Ahtisham
  • 9,170
  • 4
  • 43
  • 57
  • So many branches! After `CMP AL, 'Z'`, you could just `jna READ` and remove two `JMP` instructions. (Don't condtionally jump over a JMP unless your code gets to big that a JCC rel8 won't reach). If the first instruction after a label is a direct JMP, that means you should have just jumped to the ultimate destination in the first place. Your branch labels don't have very descriptive names, and/or your code isn't commented very well. The comments are mostly pretty trivial / local mechanical stuff and don't explain the structure of how execution flows through your code. – Peter Cordes Dec 10 '17 at 14:02
  • I edited his program to make it work. So the part which is not commented is his code. – Ahtisham Dec 10 '17 at 14:04
  • 1
    Then this is a crappy answer because you just posted a code dump without explaining which parts you changed or why. If you're not going to do that, you might as well just post good working code that doesn't have all the clunky stuff from the OP. – Peter Cordes Dec 10 '17 at 14:11
  • I did i added the comments – Ahtisham Dec 10 '17 at 14:12
  • @PeterCordes What about now ? – Ahtisham Dec 10 '17 at 15:14
  • It's an improvement. `continue1` could have a more meaningful name, like `foundUpperCase` or something. Are you sure it case-flips, though? I only see upper->lower. You just need to `xor dl, 0x20` because you've already checked that your bytes are all alphabetic characters. – Peter Cordes Dec 10 '17 at 15:23
  • 2
    Oh, I see the `sub` as well now. Way overcomplicated compared to an unconditional `xor`, but ok. The OP's code was already trying to do it the dumb way. – Peter Cordes Dec 10 '17 at 15:26
  • `continue1` renamed successfully as well as suggested :) – Ahtisham Dec 10 '17 at 15:34
  • 1
    Yup, still looking better. Still painful to read when you know that you can [detect if a byte is an ASCII alphabetic character in 4 instructions with one branch](https://stackoverflow.com/questions/35932273/how-to-access-a-char-array-and-change-lower-case-letters-to-upper-case-and-vice/35936844#35936844). I know you're just trying to stick to the OP's design, though. – Peter Cordes Dec 10 '17 at 15:38
  • 1
    It's not mine, but probably because there's barely any text outside the code block saying what was wrong or what you changed. – Peter Cordes Dec 10 '17 at 17:45
  • @PeterCordes What about now ? – Ahtisham Dec 10 '17 at 19:36
  • 2
    Now your answer is good. I hadn't read the OP's code very carefully, and until your last update the real reason for what it printed wasn't clear, but now it is. Makes perfect sense now that it reverses the string and then prints one case-flipped character. Clearly explaining what it actually did is important. (It's still a boring debugging question, though; don't expect many upvotes from anyone else!) – Peter Cordes Dec 11 '17 at 05:44
  • 1
    @PeterCordes What about now does it look better now ? – Ahtisham Dec 11 '17 at 06:47
  • @Ahtisham I am really sorry for the late response, your solution worked fine. The only question is to define a `space` character. I want to have an input of `Hello World` and an output `DLROw OLLEh`. I am using the very first code that you've posted since it makes more sense for me right now. Also, will update my question. – John E. Dec 21 '17 at 10:41
  • For handling multiple strings Just write: `cmp al, 32 je letterFound` after `je DISPLAY` instruction and `cmp bl, 32 je continue` after `cmp bl, '#' je exit` in the code that i posted. `32` is the ASCII value of Space. – Ahtisham Dec 21 '17 at 11:18
  • By the way the edit to your question is meaningless. First you ask a question then you post the **working code**. Change it back to original post. – Ahtisham Dec 21 '17 at 11:31