Search code examples
assembly8051

Is there a better method to store values in consecutive registers using indirect addressing?


I'm trying to sort an array of registers into two new odd and even registers being stored at separate locations accordingly.

I'm giving inputs manually using the memory window tab in the Keil uVision's debugger.

  • d:20H stores the length of the register array
  • d:30H is the starting address of the register array that's taken as input
  • x:2000H should be the starting address of the new odd array being created
  • x:3000H should be the starting address of the new even array being created

I've been able to figure out if a value is even or odd using the LSB. However, I've been facing trouble trying to move a register into my desired location using indirect addressing (got to do so for storing at consecutive locations). It kind of works, but right now, only the last odd/even value is being stored at x:2001H and x:3001H respectively.

Here's my attempt:

/*
program to filter an array of registers into even and odd arrays of registers 
d:20 -> length of array
d:30H -> initial input array
x:2000H -> ODD array (external memory)
x:3000H -> EVEN array (external memory)
*/

    ORG 0000H

    MOV R0, 20H ;where length of input reg array is stored
    MOV R1, #30H ;R1 will be the pointer for looping through the input array starting at d:30H
    MOV R2, #00H ;counter for incrementing DPTR for required even array index
    MOV R3, #00H ;counter for incrementing DPTR for required odd array index
LOOP:
    MOV A, @R1
    RRC A ;dividing by 2 (right shift) and moving LSB to carry

    /* alternatively:
    ANL A, #01H ;indexed element is bitwise ANDed with #01H so that only the LSB remains
    ;if LSB is 1 -> odd
    ;if LSB is 0-> even 
    and then CJNE #00H */

    MOV A, @R1 ;gotta do this cause you cant MOV @DPTR, @R1 directly
    INC R1 ;current R1 is no longer required and moving to next index for next loop
    JC ODD ;if carry is generated -> ODD -> go to ODD level
    MOV DPTR, #2000H ;starting address of even array
    JMP INCLOOP1

INCLOOP1:
    INC DPTR
    DJNZ R3, INCLOOP1 ;using this loop to increment dptr so that even values are stored in consecutive addresses
                      ;after the first even value is stored at 2000H
    MOVX @DPTR, A ;even value is moved to @DPTR(even array index)
    INC R3 ;incrementing R3 after a value is stored so that the 'ith' time an even value is found
           ;dptr is increased 'i' times
ODD:
    MOV DPTR, #3000H ;starting address of odd array
    JMP INCLOOP2

INCLOOP2:
    INC DPTR
    DJNZ R2, INCLOOP2 ;using this loop to increment dptr so that odd values are stored in consecutive addresses
                      ;after the first odd value is stored at 3000H
    MOVX @DPTR, A ;even value is moved to @DPTR(odd array index)
    INC R2
    DJNZ R0, LOOP ;loop for going through all elements of the array

    END

Ideally, DPTR should have been incremented to point to the next address every time a value is stored in the odd/even array.

Here's my inputs:

d:20H d:30H

And here's the outputs I get after running my program:

x:2000H x:3000H


Solution

  • Your code has several errors and quirks:

    • The loops incrementing DPTR decrement R2 and R3, respectively, until zero.
    • The following INC sets the value to 1, for each value to sort. This is the reason why you see only the last value in the sorted arrays.
    • The first time such a loop is entered, R2 and R3, respectively, are zero as initialized. Since DJNZ checks for zero after the decrement, these loops run 256 rounds, incrementing DPTR to 2100H and 3100H, respectively. You might want to check those XDATA locations to see if I'm right.
    • The initializing of DPTR is the other way around than your prose requires. I used the prose for my solution.
    • You can use the bit test conditional jump to decide on even/odd. There is no need for a complex alternative.
    • Before the mentioned loops there are superfluous jumps, which go to the immediately following instruction.

    This is my suggested solution, please compare it to yours.

    • It uses two register pairs (R2/R3 and R4/R5) as save storage of the output array addresses. Some 8051 derivatives have two data pointers, but the standard core has just one.
    • It uses INC DPTR, because this is shorter than other ways to increment the register pair. However, if you are perfectly sure that there will be no carry into the upper byte, you can replace that and the following MOVes by a single INC R2 (INC R4).
        ORG 0000H
    
        MOV R0, 20H ;where length of input reg array is stored
        MOV R1, #30H ;R1 will be the pointer for looping through the input array starting at d:30H
        MOV R2, #LOW 3000H
        MOV R3, #HIGH 3000H ;address for output even array
        MOV R4, #LOW 2000H
        MOV R5, #HIGH 2000H ;address for output odd array
    
    LOOP:
        MOV A, @R1 ;gotta do this cause you cant MOV @DPTR, @R1 directly
        INC R1 ;current R1 is no longer required and moving to next index for next loop
        JB ACC.0,ODD ;if LSBit is set -> ODD -> go to ODD level
    
        MOV DPL, R2
        MOV DPH, R3 ;current address in even array
        MOVX @DPTR, A ;even value is copied to even array
    
        INC DPTR
        MOV R2, DPL
        MOV R3, DPH ;increment and save address
        JMP LOOPEND
    
    ODD:
        MOV DPL, R4
        MOV DPH, R5 ;current address in odd array
        MOVX @DPTR, A ;odd value is copied to odd array
    
        INC DPTR
        MOV R4, DPL
        MOV R5, DPH ;increment and save address
    
    LOOPEND:
        DJNZ R0, LOOP ;loop for going through all elements of the array
    
        END