assemblyx86-16parity

Zero out characters with even number of set bits, and reverse string


I am not able to finish given task using DOS Debug:

Every input string symbol, that has even number of bits has to be changed to 0. And then string has to be reversed and printed to the screen.

a200
db 50

a260
db 'Enter string' 0d 0a '$'

a100
mov ah, 09
mov dx, 260
int 21
mov ah, 0a
mov dx, 200
int 21
mov ah, 02
mov dl, 0d
int 21
mov ah, 02
mov dl, 0a
int 21
xor cx, cx
mov bx, 201
mov cl, [bx]
int bx
mov dl, [bx]
inc bx
mov dl, [bx]
mov al, dl
mov ah, 0
clc
rcr al, 1
adc ah, 0

This is how far I was able to get. However, it is not finished. I am not sure if I am going to the right direction.

I have an idea to use perity flag to check if number of bits is even. However, I can't implement it.


Solution

  • int bx
    mov dl, [bx]
    inc bx
    mov dl, [bx]
    mov al, dl
    mov ah, 0
    clc
    rcr al, 1
    adc ah, 0
    

    Up to reading the length of the user inputted string, your code looks fine, but then it starts to look like you've just thrown some random stuff together!
    Your idea to use the parity flag is ok. When a byte has 0, 2, 4, 6, or 8 bits that are set (to 1), the PF will be set. When a byte has 1, 3, 5, or 7 bits that are set (to 1), the PF will be clear.
    The x86 instruction set has 4 instructions that allow you to conditionally jump based on the state of the parity flag:
    The jp and jpe instructions share the same opcode 7Ah.

    The jnp and jpo instructions share the same opcode 7Bh.

    There are many instructions that modify the parity flag. The below code uses the cmp instruction. In your program you want to zero in case of parity, which is equivalent to skip the zeroing in case of no parity. That's why the code uses the jnp instruction.

          ...
    011F  mov  cl, [bx]
    
    0121  mov  bx, 202            ; Where the string starts
    0124  cmp  byte ptr [bx], 0   ; Have the parity flag defined
    0127  jnp  012C               ; Skip in case of no parity
    0129  mov  byte ptr [bx], 0   ; Zero in case of parity
    012C  inc  bx                 ; Go to next character
    012D  loop 0124               ; Until all characters have been processed
    

    At the end of the above loop, the BX register points right behind the string. This is a good moment to apply the $-terminator that you will need in order to print your final result.

    012F  mov  byte ptr [bx], "$"
    

    The task of reversing the string requires maintaining two pointers that move towards each other while swapping bytes:

    0132  dec  bx          ; Have BX point at the last byte in the string
    0133  mov  si, 202     ; Have SI point at the first byte in the string
    0136  mov  al, [bx]    ; Read both bytes
    0138  mov  dl, [si]
    013A  mov  [bx], dl    ; Swap both bytes
    013C  mov  [si], al
    013E  inc  si          ; Move towards the middle of the string
    013F  dec  bx
    0140  cmp  si, bx      ; Stop once the pointers cross
    0142  jb   0136
    

    EDIT 1

    This edit deals with the OP's effort to implement the suggested solution.
    This is the OP's code that reportedly runs into an infinite loop:

    a200
    db 50
    
    a300
    db 'Enter string' 0d 0a '$'
    
    a100
    mov ah, 09
    mov dx, 300
    int 21
    mov ah, 0a
    mov dx, 200
    int 21
    mov ah, 02
    mov dl, 0d
    int 21
    mov ah, 02
    mov dl, 0a
    int 21
    mov bx, 202
    
    a200
    cmp byte ptr [bx], 0
    jnp 250 
    mov byte ptr [bx], 30
    
    a250
    inc bx
    loop 200
    mov byte ptr [bx], '$'
    dec bx
    mov si, 202
    
    a400
    mov al, [bx]
    mov dl, [si]
    mov [bx], dl
    mov [si], al
    inc si
    dec bx
    cmp si, bx
    jb 400
    mov dx, 202
    mov ah, 09
    int 21
    mov ah, 4c
    int 21
    
    n lab1.com
    r cx
    800
    w
    q
    

    The reasons why this fails are

    The whole code with corrections is:

    a200
    db 50 00
    
    a300
    db 'Enter string' 0D 0A '$'
    
    a100
    mov  ah, 09                    This is at address 0100
    mov  dx, 300
    int  21
    mov  ah, 0A
    mov  dx, 200
    int  21
    mov  ah, 02
    mov  dl, 0D
    int  21
    mov  ah, 02
    mov  dl, 0A
    int  21
    xor  cx, cx
    mov  bx, 201
    mov  cl, [bx]
    mov  bx, 202
    cmp  byte ptr [bx], 0          This is at address 0124
    jnp  012C
    mov  byte ptr [bx], 30
    inc  bx                        This is at address 012C
    loop 0124
    mov  byte ptr [bx], '$'
    dec  bx
    mov  si, 202
    mov  al, [bx]                  This is at address 0136
    mov  dl, [si]
    mov  [bx], dl
    mov  [si], al
    inc  si
    dec  bx
    cmp  si, bx
    jb   0136
    mov  dx, 202
    mov  ah, 09
    int  21
    mov  ah, 4C
    int  21
    
    n lab1.com
    r cx
    800
    w
    q
    

    The w command expects the file size in BX:CX. I assume you have checked BX=0 ?

    EDIT 2

    This edit deals with the OP's effort to refine the program so as to exempt the numerical digits from conversion.
    This is the OP's code that reportedly crashes:

    a200
    db 50 00
    
    a300
    db 'Enter string' 0D 0A '$'
    
    a100
    mov  ah, 09
    mov  dx, 300
    int  21
    mov  ah, 0A
    mov  dx, 200
    int  21
    mov  ah, 02
    mov  dl, 0D
    int  21
    mov  ah, 02
    mov  dl, 0A
    int  21
    xor  cx, cx
    mov  bx, 201
    mov  cl, [bx]
    mov  bx, 202
    mov  dl, byte ptr [bx]     ; Put numerical representation of character into dl register
    cmp  dl, 39                ; Compare dl value with 39 (char '9')
    jg   0132                  ; If dl value is greater, it is not a digit, jump to parity flag's definition
    cmp  dl, 30                ; Compare dl value with 30 (char '0')
    jge  013A                  ; If dl value is greater or equal then it is a digit, jump to the line where we increment bx
    cmp  byte ptr [bx], 0
    jnp  013A
    mov  byte ptr [bx], 30
    inc  bx
    loop 0124
    mov  byte ptr [bx], '$'
    dec  bx
    mov  si, 202
    mov  al, [bx]
    mov  dl, [si]
    mov  [bx], dl
    mov  [si], al
    inc  si
    dec  bx
    cmp  si, bx
    jb   0144
    mov  dx, 202
    mov  ah, 09
    int  21
    mov  ah, 4C
    int  21
    
    n lab1.com
    r cx
    800
    w
    q
    

    The problem today is that the jump targets are off by 2.
    You have added 5 lines of new code to your program. Together they take up 12 bytes, but the program seems to think it's 14 bytes.

    0124: mov  dl, byte ptr [bx]   2 bytes
    0126: cmp  dl, 39              3 bytes
    0129: jg   0132                2 bytes
    012B: cmp  dl, 30              3 bytes
    012E: jge  013A                2 bytes
    0130:                         --------
                                  12 bytes
    

    The whole code with corrections is:

    a200
    db 50 00
    
    a300
    db 'Enter string' 0D 0A '$'
    
    a100
    mov  ah, 09
    mov  dx, 300
    int  21
    mov  ah, 0A
    mov  dx, 200
    int  21
    mov  ah, 02
    mov  dl, 0D
    int  21
    mov  ah, 02
    mov  dl, 0A
    int  21
    xor  cx, cx
    mov  bx, 201
    mov  cl, [bx]
    mov  bx, 202
    
    0124: mov  dl, [bx]
    0126: cmp  dl, 39
    0129: ja   0130                  ERR jg   0132
    012B: cmp  dl, 30
    012E: jae  0138                  ERR jge  013A
    0130: cmp  byte ptr [bx], 0
    0133: jnp  0138                  ERR jnp  013A
    0135: mov  byte ptr [bx], 30
    0138: inc  bx
    0139: loop 0124
    013B: mov  byte ptr [bx], '$'
    013E: dec  bx
    013F: mov  si, 202
    0142: mov  al, [bx]
    0144: mov  dl, [si]
    0146: mov  [bx], dl
    0148: mov  [si], al
    014A: inc  si
    014B: dec  bx
    014C: cmp  si, bx
    014E: jb   0142                  ERR jb   0144
    
    mov  dx, 202
    mov  ah, 09
    int  21
    mov  ah, 4C
    int  21
    
    n lab1.com
    r cx
    800
    w
    q
    

    Tip1: ASCII codes are unsigned numbers. Therefore you should use the unsigned conditional branch instructions. I've used ja JumpIfAbove and jae JumpIfAboveOrEqual instead of jg JumpIfGreater and jge JumpIfGreaterOrEqual.
    Tip2: If you would use AL instead of DL in those few new lines then the program would shorten by another 2 bytes. You could do this as an exercise, but remember that the branch targets would have to change accordingly, again!

    0124: mov  al, [bx]   2 bytes
    0126: cmp  al, 39     2 bytes
    0128: ja   012E       2 bytes
    012A: cmp  al, 30     2 bytes
    012C: jae  0136       2 bytes
    012E:                --------
                         10 bytes