Last night before I left I started having a go at trying to write the C++ equivalent of the assembly code, but I only got so far.
I’m not even sure how much of this is right to be honest…
// r0 = source, r1 = destination
void unlz4(const std::uint8_t * source, std::uint8_t * destination)
{
// Get length of data
// LDRH r2, [r0]
// Advance source pointer, R0 += 2
// ADDS r0, r0, #2
// The actual code is more like const std::uint16_t length = *reinterpret_cast<const std::uint16_t *>(source);
// but that would be in the realm of undefined behaviour
const std::uint16_t length = ((source[0] << 8) | (source[1] << 0));
unlz4_length(&source[2], destination, length);
}
// r0 = source, r1 = destination, r2 = length
void unlz4_length(const std::uint8_t * source, std::uint8_t * destination, std::uint32_t length)
{
// PUSH {r4-r6,lr}
// ADDS r5, r2, r0
std::uint32_t r5 = &source[length];
getToken:
// R6 = *R0
// LDRB r6, [r0]
std::uint8_t r6 = *source;
// ++R0
// ADDS r0, r0, #1
source += 1;
// R4 = R6 >> 4
// LSRS r4, r6, #4
std::uint32_t r4 = (r6 >> 4);
// BEQ getOffset
if(r4 == 0)
goto getOffset;
// BL getLength
// call getLength;
// MOVS R2, R0
// ???
// BL copyData
// call copyData
// MOVS R0, R2
// ???
getOffset:
// R3 = R0[0]
// LDRB r3, [r0, #0]
// R2 = R1 - R3
// SUBS r2, r1, r3
// R3 = R0[1]
// LDRB r3, [r0, #1]
// R3 = (R3 << 8)
// LSLS r3, r3, #8
// R2 -= R3
// SUBS r2, r2, r3
// R0 += 2
// ADDS r0, r0, #2
// R4 = R6 & 0x3
// LSLS r4, r6, #28
// LSRS r4, r4, #28
// call getLength
// BL getLength
// R4 += 4
// ADDS r4, r4, #4
// BL copyData
// CMP R0, R5
// BLT getToken
// POP {r4-r6,pc}
//getLength:
//CMP r4, #0x0F
//BNE gotLength
if(r4 == 15)
{
//getLengthLoop:
//LDRB r3, [r0]
//ADDS r0, r0, #1
//ADDS r4, r4, r3
//CMP r3, #0xFF
//BEQ getLengthLoop
do
{
const std::uint8_t r3 = *r0;
r0 += 1;
r4 += r3;
}
while(r3 == 0xFF);
//gotLength:
}
// return;
//BX LR
}
// R1, R2, R4
void copyData(const std::uint8_t * source, std::uint8_t * destination, std::int32_t length)
{
// R4 = -R4
// RSBS r4, r4, #0
// R2 -= R4
// SUBS r2, r2, r4
// R1 -= R4
// SUBS r1, r1, r4
length = (0 - length);
destination = (destination - length);
source = (source - length);
// copyDataLoop:
do
{
// R3 = R2[R4]
//LDRB r3, [r2, r4]
// R1[R4] = R3
//STRB r3, [r1, r4]
// R4 += 1;
//ADDS r4, r4, #1
const std::uint8_t value = *(destination + length);
*(source + length) = value;
length = (length + 1);
}
while(length != 0);
// BNE copyDataLoop
// return;
// BX lr
}
So far the thing I’m most surprised about is copyData
.
It all seems technically valid, but it’s a really backwards way to go about things.
I get that they’re probably trying to use fewer instructions, but even so.
I probably won’t finish implementing the rest, so make of it what you will.
(Bear in mind that even if this compiles it probably won’t do anything worthwhile yet.)
Not really relevant, but for some reason I’ve always found that lowercase assembly looks odd.
I tend to think it looks better when it’s uppercase.
I don’t know why that should be.