I'm reverse-engineering a DOS binary that was compiled with Borland C++ 3.0, and I'm trying to create C code that will produce the same assembly as the original when compiled with the same compiler. For the majority of the code so far, this works out fairly well, but there is one place that's really puzzling. I just can't figure out what kind of source code would get the compiler to produce this particular asm sequence.
So the relevant part of the original assembly is:
; function's stack layout:
vram_ptr = dword ptr -4
target_offset = word ptr 6
size = word ptr 8
src_data = dword ptr 0Ah
; the funtion also uses registers SI and DI to hold two loop variables.
; ----------------------------------------------------
; The code in question. It's basically just
; vram_ptr[i] = *src_data, but done in a very
; convoluted manner
; ----------------------------------------------------
les bx, [bp+vram_ptr] ; load vram_ptr into ES:BX
push es
mov es, word ptr [bp+src_data+2] ; set ES to FP_SEG(src_data)
push bx
mov bx, word ptr [bp+src_data] ; set BX to FP_OFF(src_data)
mov al, es:[bx] ; basically _AL = *src_data
pop bx
pop es
mov es:[bx+si], al ; Store it
; ----------------------------------------------------
; ----------------------------------------------------
; src_data++
inc word ptr [bp+src_data]
When I do the most obvious thing and write dest[i] = *data;
in C, the compiler generates something much more reasonable, but sadly different from the original:
les bx,[vram_ptr] ; same as original
push es ; same as original
les si,[src_data] ; different, original avoids
mov al,[es:si] ; use of SI
pop es ; same as original
mov [es:bx+di],al ; same as original
inc word [src_data] ; same as original
And it also spills a loop variable to the stack that is held in SI
in the original code, and uses DI
instead of SI
for the loop variable used in the expression. But no matter what I try, I can't get it to generate the original assembly.
It seems as though the compiler was somehow prevented from using SI
in the data move expression, and was thus forced to generate the more complicated sequence.
What kind of source code and/or compiler flags might cause this?
For reference, here's the full C code I have so far (which is close to the original asm aside from the mismatches I mentioned):
void pascal CopyToVram(byte far* data, word size, word destOffset)
{
register word i;
register word mask;
byte far* dest = MK_FP(0xa000, destOffset);
// These macros just generate a bunch of port IO instructions
EGA_SET_DEFAULT_MODE();
EGA_SET_DEFAULT_BITMASK();
for (i = 0; i < size; i++)
{
for (mask = 0x0100; mask < 0x1000; mask <<= 1)
{
disable();
outport(0x03c4, mask | 0x02);
// TODO: Codegen mismatch
dest[i] = *data;
data++;
enable();
}
}
}
REP STOSB
and the like.