We have a problem with a firmware project in 8051 assembly. It is used in an embedded system and now needs to be adapted to changes in the hardware. It contains a subroutine that sequentially reads bits from a shift register and puts them into a data segment of fixed length. These bits are then used as the basis for calculations that are too complex to describe in this post. Due to the hardware changes we no longer get these bits in sequential order. The order has been reversed and each block of eight is permuted following a complex logic. We had problems implementing the adaption to the new bit order in the assembly.
The original subroutine would roughly look like this if implemented in C:
char TimeBuffer[LEN];
char DataBuffer[LEN];
for (int i = 0; i < LEN; i++)
{
if (TimeBuffer[i] != 0)
{
DataBuffer[i] == GPIO_Read();
}
}
The re-implementation would look like this in C:
char TimeBuffer[LEN];
char DataBuffer[LEN];
int NewIndex;
for (int i = 0; i < LEN; i++)
{
NewIndex = GetNewIndex(i);
if (TimeBuffer[NewIndex] != 0)
{
DataBuffer[NewIndex] == GPIO_Read();
}
}
Our problem revolves around pointer arithmetic. The old implementation simply used the statement INC DPTR to move to the next byte in both data segments after each loop cycle. The new implementation requires that we re-initialize the DPTR for both data segments at the beginning of each loop cycle. This re-initialization consists of first calling a subroutine that calculates the new index. Then this new index is added to the start address of both data segments.
We already have an implementation of this new solution and it gives us no errors or warnings in Keil uVision 2. However it does not give us the expected results. As we have no way of debugging the project in the IDE we can only compile new versions of the firmware and see what it does when it is running on the embedded device.
The last thing we tried was to take out the CALL of the subroutine that gives us the new index because we wanted to exclude the subroutine as the point of failure. Instead of the new index we are now adding a value to the pointers that is simply increased after each loop cycle. The result that we expected to see is that this would behave just like the original implementation. However it behaves in the same wrong way as the version that still has the CALL of the subroutine and uses its result.
We now suspect that we have some fundamental error in the way we add the index to the pointers. We have double - and triple checked it and cannot find the mistake. This is where we would like to ask for help. These are the relevant parts of the original version in the 8051 assembly:
MOV DPS,#1
MOV DPTR,#DataBuffer
MOV DPS,#0
mov dptr,#TimeBuffer
mov R0,LEN
readD:
MOV C,P1.1 ; read data pin of shift register
JNC shiftD
MOV DPS,#0
movx a,@dptr
JNZ shiftD
MOV DPS,#1
movx a,@dptr
INC ACC
MOVX @DPTR,A
shiftD:setb P1.0 ; clock pin high of shift register
clr P1.0 ; clock pin low of shift register
MOV DPS,#0
inc dptr
MOV DPS,#1
INC DPTR
djnz R0,readD
This is what it looks like now, with our recent changes:
;MOV DPS,#1
;MOV DPTR,#DataBuffer
;MOV DPS,#0
;mov dptr,#TimeBuffer
mov R0,LEN
mov R1,#0h
leseD:
MOV R7,REG1
MOV DPS,#1
MOV DPTR,#DataBuffer
MOV A,DPL
ADD A,R7
MOV DPL,A
MOV A,DPH
ADDC A,#0h
MOV DPH,A
MOV DPS,#0
mov dptr,#TimeBuffer
MOV A,DPL
ADD A,R7
MOV DPL,A
MOV A,DPH
ADDC A,#0h
MOV DPH,A
MOV C,P1.1 ; read data pin of shift register
JNC shiftD
MOV DPS,#0
movx a,@dptr
JNZ shiftD
MOV DPS,#1
movx a,@dptr
INC ACC
MOVX @DPTR,A
shiftD:setb P1.0 ; clock pin high of shift register
clr P1.0 ; clock pin low of shift register
;MOV DPS,#0
;inc dptr
;MOV DPS,#1
;INC DPTR
inc R1
djnz R0,readD
Could you please point us to any mistakes you see in the part that calculates the pointers or elsewhere?
Meanwhile, we have found the solution on our own. Due to platform limitations, no one in our company knew this kind of implementation was impossible.
It turns out that, while you can switch the data segment that DPTR refers to, by moving a new value into DPS, both DPL and DPH remain unaffected by it and always point to the first data segment. As a consequence, we were always writing to TimeBuffer without knowing it.
We found this out after we sent the values of the pointers to a terminal emulator via the serial port on the embedded device.