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.
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 ecx 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;
}
Related
There is a code excerpt from official Quake 2 source code:
unsigned *buf;
dheader_t header;
...
header = *(dheader_t *)buf; // #1
for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
((int *)&header)[i] = LittleLong ( ((int *)&header)[i]); // #2
Can someone please explain me in the most possible details what do the line #1 and then #2 really do because I'm little or more confused...
P.S
Here is the rest of the definitions if it helps:
int LittleLong (int l) {return _LittleLong(l);}
...
typedef struct
{
int ident;
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;
P.S. 2
I've linked above the original full source file code if needed.
This is some seriously brittle code and you shouldn't write code like this.
What it does is to go through the struct int by int, then does something with each such int inside _LittleLong. Very likely this function performs a 32 bit conversion from a big endian integer to a little endian one. Meaning that the source you are looking at is likely something related to reception of IP packages.
Checking at what the code does step by step:
for (i=0 ; i<sizeof(dheader_t)/4 ; i++) is a sloppier way of writing sizeof(dheader_t)/sizeof(int). That is: iterate through the struct int by int, chunks of 32 bits.
(int *)&header converts from a dheader_t* to a int*. This is actually well-defined by a special rule in C that allows us to convert from a pointer to a struct to a pointer to its first member or vice versa and the first member is int.
However, doing so is only well-defined for the first member. Instead they take the converted int* and apply array dereferencing on it: ((int *)&header)[i]. This is undefined behavior in C, a so-called strict aliasing violation, and could also cause alignment problems in some situations. Bad.
The int read from the struct through this dereferencing is then passed along to LittleLong which very likely does a big -> little endian conversion.
((int *)&header)[i] = and here it is written back to where it was grabbed from.
Better, safer, well-defined and possibly faster code could look like:
void endianify (dheader_t* header)
{
_Static_assert(sizeof(dheader_t)%sizeof(uint32_t)==0,
"Broken struct: dheader_t");
unsigned char* start = (unsigned char*)header;
unsigned char* end = start + sizeof(dheader_t);
for(unsigned char* i=start; i!=end; i+=sizeof(uint32_t))
{
uint32_t tmp;
memcpy(&tmp,i,sizeof(uint32_t));
i[0]= (tmp >> 24) & 0xFF;
i[1]= (tmp >> 16) & 0xFF;
i[2]= (tmp >> 8) & 0xFF;
i[3]= (tmp >> 0) & 0xFF;
}
}
Disassembly:
endianify:
mov eax, DWORD PTR [rdi]
bswap eax
mov DWORD PTR [rdi], eax
mov eax, DWORD PTR [rdi+4]
bswap eax
mov DWORD PTR [rdi+4], eax
mov eax, DWORD PTR [rdi+8]
bswap eax
mov DWORD PTR [rdi+8], eax
mov eax, DWORD PTR [rdi+12]
bswap eax
mov DWORD PTR [rdi+12], eax
mov eax, DWORD PTR [rdi+16]
bswap eax
mov DWORD PTR [rdi+16], eax
ret
I don't understand what is the problem because the result is right, but there is something wrong in it and i don't get it.
1.This is the x86 code I have to convert to C:
%include "io.inc"
SECTION .data
mask DD 0xffff, 0xff00ff, 0xf0f0f0f, 0x33333333, 0x55555555
SECTION .text
GLOBAL CMAIN
CMAIN:
GET_UDEC 4, EAX
MOV EBX, mask
ADD EBX, 16
MOV ECX, 1
.L:
MOV ESI, DWORD [EBX]
MOV EDI, ESI
NOT EDI
MOV EDX, EAX
AND EAX, ESI
AND EDX, EDI
SHL EAX, CL
SHR EDX, CL
OR EAX, EDX
SHL ECX, 1
SUB EBX, 4
CMP EBX, mask - 4
JNE .L
PRINT_UDEC 4, EAX
NEWLINE
XOR EAX, EAX
RET
2.My converted C code, when I input 0 it output me the right answer but there is something false in my code I don't understand what is:
#include "stdio.h"
int main(void)
{
int mask [5] = {0xffff, 0xff00ff, 0xf0f0f0f, 0x33333333, 0x55555555};
int eax;
int esi;
int ebx;
int edi;
int edx;
char cl = 0;
scanf("%d",&eax);
ebx = mask[4];
ebx = ebx + 16;
int ecx = 1;
L:
esi = ebx;
edi = esi;
edi = !edi;
edx = eax;
eax = eax && esi;
edx = edx && edi;
eax = eax << cl;
edx = edx >> cl ;
eax = eax || edx;
ecx = ecx << 1;
ebx = ebx - 4;
if(ebx == mask[1]) //mask - 4
{
goto L;
}
printf("%d",eax);
return 0;
}
Assembly AND is C bitwise &, not logical &&. (Same for OR). So you want eax &= esi.
(Using &= "compound assignment" makes the C even look like x86-style 2-operand asm so I'd recommend that.)
NOT is also bitwise flip-all-the-bits, not booleanize to 0/1. In C that's edi = ~edi;
Read the manual for x86 instructions like https://www.felixcloutier.com/x86/not, and for C operators like ~ and ! to check that they are / aren't what you want. https://en.cppreference.com/w/c/language/expressions https://en.cppreference.com/w/c/language/operator_arithmetic
You should be single-stepping your C and your asm in a debugger so you notice the first divergence, and know which instruction / C statement to fix. Don't just run the whole thing and look at one number for the result! Debuggers are massively useful for asm; don't waste your time without one.
CL is the low byte of ECX, not a separate C variable. You could use a union between uint32_t and uint8_t in C, or just use eax <<= ecx&31; since you don't have anything that writes CL separately from ECX. (x86 shifts mask their count; that C statement could compile to shl eax, cl. https://www.felixcloutier.com/x86/sal:sar:shl:shr). The low 5 bits of ECX are also the low 5 bits of CL.
SHR is a logical right shift, not arithmetic, so you need to be using unsigned not int at least for the >>. But really just use it for everything.
You're handling EBX completely wrong; it's a pointer.
MOV EBX, mask
ADD EBX, 16
This is like unsigned int *ebx = mask+4;
The size of a dword is 4 bytes, but C pointer math scales by the type size, so +1 is a whole element, not 1 byte. So 16 bytes is 4 dwords = 4 unsigned int elements.
MOV ESI, DWORD [EBX]
That's a load using EBX as an address. This should be easy to see if you single-step the asm in a debugger: It's not just copying the value.
CMP EBX, mask - 4
JNE .L
This is NASM syntax; it's comparing against the address of the dword before the start of the array. It's effectively the bottom of a fairly normal do{}while loop. (Why are loops always compiled into "do...while" style (tail jump)?)
do { // .L
...
} while(ebx != &mask[-1]); // cmp/jne
It's looping from the end of the mask array, stopping when the pointer goes past the end.
Equivalently, the compare could be ebx !-= mask - 1. I wrote it with unary & (address-of) cancelling out the [] to make it clear that it's the address of what would be one element before the array.
Note that it's jumping on not equal; you had your if()goto backwards, jumping only on equality. This is a loop.
unsigned mask[] should be static because it's in section .data, not on the stack. And not const, because again it's in .data not .rodata (Linux) or .rdata (Windows))
This one doesn't affect the logic, only that detail of decompiling.
There may be other bugs; I didn't try to check everything.
if(ebx != mask[1]) //mask - 4
{
goto L;
}
//JNE IMPLIES a !=
I am completing an assignment related to c programming and assembly language. Here is the simple c program :
int multiply(int a, int b) {
int k = 4;
int c,d, e;
c = a*b ;
d = a*b + k*c;
return d;
}
And it's optimised assembly is
_a$ = 8 ; size = 4
_b$ = 12 ; size = 4
_multiply PROC
mov eax, DWORD PTR _a$[esp-4]
imul eax, DWORD PTR _b$[esp-4]
lea eax, DWORD PTR [eax+eax*4]
ret 0
_multiply ENDP
I want to know the value of eax register after this line of code in assembly
lea eax, DWORD PTR [eax+eax*4]
I know when add integers in assembly, it stores result in the destination. and when we multiply it stores in eax. so if I call the function multiply( 3 , 8 ), the value of eax register after that line should be 120. Am I correct?
lea is "load effective address".
Instruction sets can have some quite complex multi-register address calculation modes that are generally used just for reading and writing data to memory, but lea allows the programmer to get the address that would be accessed by the instruction.
Effectively, it performs the calculation inside the bracket, returns that value - it doesn't access the memory (which is what bracket usually implies).
In this case it is being used as a quick way to multiply by 5, because the rest of the function has been optimised away!
I'm very new to Assembly and the code below is supposed to swap two integers via two different functions: first using swap_c and then using swap_asm.
However, I doubt, whether I need to push (I mean save) each value of registers before assembly code and pop them later (just before returning to main). In other words, will the CPU get mad at me if I return different register content (not the crucial ones like ebp or esp; but, just eax, ebx, ecx & edx) after running swap_asm function? Is it better to uncomment the lines in the assembly part?
This code runs OK for me and I managed to reduce the 27 lines of assembled C code down to 7 Assembly lines.
p.s.: System is Windows 10, VS-2013 Express.
main.c part
#include <stdio.h>
extern void swap_asm(int *x, int *y);
void swap_c(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
int main(int argc, char *argv[]) {
int x = 3, y = 5;
printf("before swap => x = %d y = %d\n\n", x, y);
swap_c(&x, &y);
printf("after swap_c => x = %d y = %d\n\n", x, y);
swap_asm(&x, &y);
printf("after swap_asm => x = %d y = %d\n\n", x, y);
getchar();
return (0);
}
assembly.asm part
.686
.model flat, c
.stack 100h
.data
.code
swap_asm proc
; push eax
; push ebx
; push ecx
; push edx
mov eax, [esp] + 4 ; get address of "x" stored in stack into eax
mov ebx, [esp] + 8 ; get address of "y" stored in stack into ebx
mov ecx, [eax] ; get value of "x" from address stored in [eax] into ecx
mov edx, [ebx] ; get value of "y" from address stored in [ebx] into edx
mov [eax], edx ; store value in edx into address stored in [eax]
mov [ebx], ecx ; store value in ecx into address stored in [ebx]
; pop edx
; pop ecx
; pop ebx
; pop eax
ret
swap_asm endp
end
Generally, this depends on the calling convention of the system you are working on. The calling convention specifies how to call functions. Generally, it says where to put the arguments and what registers must be preserved by the called function.
On i386 Windows with the cdecl calling convention (which is the one you probably use), you can freely overwrite the eax, ecx, and edx registers. The ebx register must be preserved. While your code appears to work, it mysteriously fails when a function starts to depend on ebx being preserved, so better save and restore it.
I'm developing with VS 2012, under 64 bit OS but output executable is 32 bit. Recently I was fiddling with swap algorithm. I did achieve it on C by;
void swap(int *p1, int *p2)
{
*p1 = *p1 + *p2;
*p2 = *p1 - *p2;
*p1 = *p1 - *p2;
}
But I also want to do that with inline assembly (__asm). I tried with these codes;
void aswap(int *p1, int *p2)
{
__asm {
mov ebx, p1
mov ecx, p2
mov p1, ecx
mov p2, ebx
}
}
and test the aswap() with these codes;
int a = 10;
int b = 200;
// print out the initial values
printf("%x(%d) %x(%d)\n", &a, a, &b, b);
aswap(&a, &b);
// finally print out the swapped values
printf("%x(%d) %x(%d)\n", &a, a, &b, b);
but the individual addresses and (consequently) the values are not changes.
Please help me. Thanks in advance.
Update 1: #ouah
I've tested the swap() with these codes;
#include <limits>
int a = INT_MAX;
int b = INT_MAX - 1;
...
and here is my screenshot. Did I do something illegal with the memory?
The classic way to do this is to do an inline swap with several XOR operations. Assuming you have one pointer in EAX and the other in EBX the code would be:
XOR EAX, EBX
XOR EBX, EAX
XOR EAX, EBX
The result is that what was in EAX is now in EBX, and what was in EBX is now in EAX. Follow the bouncing bits to figure out how this works (and it's done entirely in registers, requiring no additional memory).
Share and enjoy.