BPL BPL - 1 month ago 17
C Question

C Implementation of nasm code

I'd like to start converting a little nasm project {synth.asm, synth_core.nh} to c to learn a little bit more about that little soft-synthesizer.

Problem is my asm knowledge is very very rusty, I'm wondering where to start off. I thought maybe one decompiler could help me out but I haven't found anything open-source able to convert these simple nasm listings to c.

Another alternative would be doing the conversion asm->c manually but I'm struggling to understand one of the most simplest functions :(

ie:

;distortion_machine
;---------------------------
;float a
;float b
;---------------------------
;ebp: distort definition
;edi: stackptr
;ecx: length
section distcode code align=1
distortion_machine:
pusha
add ecx, ecx
.sampleloop:
fld dword [edi]
fld dword [ebp+0]
fpatan
fmul dword [ebp+4]
fstp dword [edi]
scasd
loop .sampleloop
popa
add esi, byte 8
ret


broken attempt:

void distortion_machine(???) { // pusha; saving all registers
int ecx = ecx+ecx; // add ecx, ecx; this doesn't make sense

while(???) { // .sampleloop; what's the condition?
float a = [edi]; // fld dword [edi]; docs says edi is stackptr, what's the meaning?
float b = [ebp+0]; // fld dword [ebp+0]; docs says ebp is distort definition, is that an input parameter?
float c = atan(a,b); // fpatan;
float d = c*[ebp+4]; // fmul dword [ebp+4];
// scasd; what's doing this instruction?
}

return ???;

// popa; restoring all registers
// add esi, byte 8;
}


I guess the above nasm listing is a very simple loop distorting a simple audio buffer but I don't understand which ones are the inputs and which ones are the outputs, I don't even understand the loop conditions :')

Any help with the above routine and how to progress with this little educational project would be really appreciated.

Answer

There's a bit of guesswork here:

;distortion_machine
;---------------------------
;float a << input is 2 arrays of floats, a and b, successive on stack
;float b
;---------------------------
;ebp: distort definition  << 2 floats that control distortion
;edi: stackptr            << what it says
;ecx: length              << of each input array (a and b)
section distcode code align=1
distortion_machine:
    pusha        ; << save all registers
    add ecx, ecx ; << 2 arrays, so double for element count of both
    .sampleloop:
        fld dword [edi]    ; << Load next float from stack
        fld dword [ebp+0]  ; << Load first float of distortion control
        fpatan             ; << Distort with partial atan.
        fmul dword [ebp+4] ; << Scale by multiplying with second distortion float
        fstp dword [edi]   ; << Store back to same location
        scasd              ; << Funky way to incremement stack pointer
    loop .sampleloop       ; << decrement cx and jump if not zero
    popa                   ; << restore registers
    add esi, byte 8        ; << See call site. si purpose here isn't stated 
ret

It's a real guess, but esi may be a separate argument stack pointer, and the addresses of a and b have been pushed there. This code ignores them by making assumptions about the data stack layout, but it still needs to remove those pointers from the arg stack.

Approximate C:

struct distortion_control {
  float *level;
  float *scale;
};

// Input: float vectors a and b stored consecutively in buf.
void distort(struct distortion_control *c, float *buf, unsigned buf_size) {
  buf_size *= 2;
  do { // Note both this and the assembly misbehave if buf_size==0
    *buf = atan2f(*buf, c->level) * c->scale;
    ++buf;
  } while (--buf_size);
}

In a C re-implementation, you'd probably want to be more explicit and fix the zero-size buffer bug. It wouldn't cost much:

void distort(struct distortion_control *c, float *a, float *b, unsigned size) {
  for (unsigned n = size; n; --n, ++a) *a = atan2f(*a, c->level) * c->scale;
  for (unsigned n = size; n; --n, ++b) *b = atan2f(*b, c->level) * c->scale;
}