I've been playing with DOS real mode assembly for a while and now I want to utilize some routines in a C program. I'm using Turbo C 2.01 and TASM 3.0. I'm however unable to modify a variable passed by address, see the _setval routine below. I don't need/want inline assembly. A simple example:
foo.c
#include <stdio.h>
extern void setval(int *x, int *y);
extern int sum(int x, int y);
int main()
{
int result, a, b;
result = a = b = 0;
setval(&a, &b);
result = a + b;
printf("a+b=%i, a=%i, b=%i\n", result, a, b);
result = 0;
a = 42;
b = 19;
result = sum(a, b);
printf("a+b=%i, a=%i, b=%i\n", result, a, b);
return 0;
}
foortn.asm
public _setval
public _sum
.model small
.stack
.data
.code
_setval proc near
push bp
mov bp, sp
mov word ptr [bp+4], 42
mov word ptr [bp+6], 19
pop bp
ret
endp
_sum proc near
push bp
mov bp, sp
mov ax, word ptr [bp+4]
add ax, word ptr [bp+6]
pop bp
ret
endp
end
I compile it like this:
tcc -c -ms foo.c
tasm /ml foortn.asm
tcc foo.obj foortn.obj
The result is:
a+b=0, a=0, b=0
a+b=61, a=42, b=19
I'm obviously missing something, but what?
Hans, Mark and Bill, thank you very much for your prompt and helpful responses.
Your current code is overwriting the passed pointer. You need to retrieve the pointer and write through it. Something like this:
mov ax, word ptr [bp+4]
mov word ptr [ax], 42
Write this code in C first and look at the assembly code that it generates to get this right.
Try replacing:
mov word ptr [bp+4], 42
mov word ptr [bp+6], 19
with
mov bx, word ptr [bp+4]
mov [bx], 42
mov bx, word ptr [bp+6]
mov [bx], 19
This:
mov word ptr [bp+4], 42
mov word ptr [bp+6], 19
is writing to the stack, not the addresses on the stack. You'll need to read the addresses on the stack, then write to them instead:
mov bx,[bp+4] ; get the address of (a)
mov [bx],42 ; Write to that address
mov bx,[bp+6] ; (b)
mov [bx],19 ; write
I don't know assembler ... but C passes everything by value.
In sum [bp+4] is 42 (or 19); in addsetval [bp+4] is 0xDEADBEEF 0xDECAFBAD (or whatever)
Related
I have a problem with the below code:
void swap(int* a, int* b) {
__asm {
mov eax, a;
mov ebx, b;
push[eax];
push[ebx];
pop[eax];
pop[ebx];
}
}
int main() {
int a = 3, b = 6;
printf("a: %d\tb: %d\n", a, b);
swap(&a, &b);
printf("a: %d\tb: %d\n", a, b);
}
I am running this code in visual studio and when I run this, it says:
Run-Time check failure- The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
What am I missing?
To answer the title question: make sure you balance pushes and pops. (Normally getting that wrong would just crash, not return with the wrong ESP). If you're writing a whole function in asm make sure ret 0 or ret 8 or whatever matches the calling convention you're supposed to be using and the amount of stack args to pop (e.g. caller-pops cdecl ret 0 or callee-pops stdcall ret n).
Looking at the compiler's asm output (e.g. on Godbolt or locally) reveals the problem: different operand-sizes for push vs. pop, MSVC not defaulting to dword ptr for pop.
; MSVC 19.14 (under WINE) -O0
_a$ = 8 ; size = 4
_b$ = 12 ; size = 4
void swap(int *,int *) PROC ; swap
push ebp
mov ebp, esp
push ebx ; save this call-preserved reg because you used it instead of ECX or EDX
mov eax, DWORD PTR _a$[ebp]
mov ebx, DWORD PTR _b$[ebp]
push DWORD PTR [eax]
push DWORD PTR [ebx]
pop WORD PTR [eax]
pop WORD PTR [ebx]
pop ebx
pop ebp
ret 0
void swap(int *,int *) ENDP
This code would just crash, with ret executing while ESP points to the saved EBP (pushed by push ebp). Presumably Visual Studio passes addition debug-build options to the compiler so it does more checking instead of just crashing?
Insanely, MSVC compiles/assembles push [reg] to push dword ptr (32-bit operand-size, ESP-=4 each), but pop [reg] to pop word ptr (16-bit operand-size, ESP+=2 each)
It doesn't even warn about the operand-size being ambiguous, unlike good assemblers such as NASM where push [eax] is an error without a size override. (push 123 of an immediate always defaults to an operand-size matching the mode, but push/pop of a memory operand usually needs a size specifier in most assemblers.)
Use push dword ptr [eax] / pop dword ptr [ebx]
Or since you're using EBX anyway, not limiting your function to just the 3 call-clobbered registers in the standard 32-bit calling conventions, use registers to hold the temporaries instead of stack space.
void swap_mov(int* a, int* b) {
__asm {
mov eax, a
mov ebx, b
mov ecx, [eax]
mov edx, [ebx]
mov [eax], edx
mov [ebx], ecx
}
}
(You don't need ; empty comments at the end of each line. The syntax inside an asm{} block is MASM-like, not C statements.)
im having a small issue with my assembly function that i made.
; Im failing super hard at writing this
; Function.
.MODEL c, small
.DATA?
.DATA
curpos_ PROTO C columns_:BYTE, rows_:BYTE
.CODE
public curpos_
curpos_ PROC C columns_:BYTE, rows_:BYTE
mov dh, columns_
mov dl, rows_
mov bh, 0
mov ah, 2
int 10h
ret
curpos_ ENDP
END
And my C file in which i prototype the assembly function.
#include<stdio.h>
#include<conio.h>
#include<math.h>
void clrscr(void);
extern char _columns, _rows;
extern void _curpos(char _columns, char _rows);
void arcradius() {
float w;
float h;
float radi = w / 2.0;
float value;
clrscr();
...
_curpos(20,40);
getch();
arcradius();
}
The issue im having is that my C program is not giving the assembly function the right arguments, in my C file i use _curpos(20,40) but it doesn't use the values in the parentheses. Instead it uses some garbage number from the previous scanf(); input.
Is there something im mis-declaring, prototyping incorrectly, or forgot?
I'm using OpenWatcom and MASM 6.11.
Thanks, Noah "MadDog" Buzelli
EDIT:
Here is the fixed assembly function
; Im failing super hard at writing this
; Function.
.MODEL small
.DATA?
.DATA
curpos PROTO C _columns:BYTE, _rows:BYTE
.CODE
public curpos
curpos PROC C _columns:BYTE, _rows:BYTE
push bx
mov dh, BYTE PTR _columns
mov dl, BYTE PTR _rows
mov bh, 0
mov ah, 2
int 10h
pop bx
ret 4
curpos ENDP
END
And here is my C prototype
extern void __stdcall curpos(char columns, char rows);
Thank you Mgetz :)
So you need to specify the Calling convention for the assembly method. By default open watcom uses __stdcall
So it should look something like this:
extern void __stdcall curpos(char _columns, char _rows);
This asm is probably right, but untested. We're doing everything manually, not relying on MASM's magic to set up BP and calculate the position of args on the stack.
_columns$ = 4 ; size = 1
_rows$ = 6 ; offsets relative to the frame pointer
; saved-BP at [bp+0], ret addr at [bp+2], first arg at [bp+4]
_curpos#4 PROC ; COMDAT
push bp
mov bp, sp ; for access to args on the stack, [sp] isn't valid
push bx ; save/restore the caller's BX
mov dh, BYTE PTR _columns$[bp]
mov dl, BYTE PTR _rows$[bp]
mov bh, 0 ; page=0
mov ah, 2
int 10h ; int 10h / AH=2 - BIOS Set cursor position
pop bx
pop bp ; no mov sp,bp needed, SP is already good
ret 4 ; pop ret addr, then SP+=4
_curpos#4 ENDP
For what it's worth it may be better to use inline assembly for this instead of doing a full implementation in asm. If you use inline asm the compiler takes care of all of this, you just have to get the inputs into registers, not write code that returns.
I am writing a small ASM/C-Program for calculating the number of dividers of a number. I got the following C function:
#include <stdio.h>
extern void getDivisorCounter(int value, int* result);
int main(int argc, char** argv) {
int number;
printf("Please insert number:\n");
scanf("%d", &number);
int* result;
getDivisorCounter(number, result);
printf("amount of div: %d\n", *result);
return 0;
}
where I receive a result from the following assembler programm:
section .text
global getDivisorCounter
getDivisorCounter:
push ebp
mov ebp, esp
mov ecx, [ebp+8]
mov eax, 0
push ebx
for_loop:
mov ebx, ecx
jmp checking
adding:
add ebx, ecx
checking:
cmp ebx, [ebp+8]
jg looping
jl adding
inc eax
looping:
loop for_loop
mov [ebp+12], eax
pop ebx
pop ebp
ret
From Debugging, I know, that I end up with the right value in eax. But somehow I cannot get it to be printed by my C programm.
Could you give me a hint on how to solve this?
If neccessary, I am using NASM and GCC.
You do not need a pointer for this. Anyway, if you (or the assignment) insist, you must 1) initialize said pointer on the C side and 2) write through that pointer on the asm side.
E.g.
int value;
int* result = &value;
and
mov ecx, [ebp+12]
mov [ecx], eax
If you must use a pointer, this does not mean you need to create an extra pointer variable. You can just pass the address of a variable of proper type. This would eliminate the risk of missing memory allocation.
Missing memory allocation is the reason for your problem. result does not point to valid memory.
Instead of
int val;
int *result = &val; // <<== note the mandatory initialization of your pointer.
getDivisorCounter(number, result);
printf("amount of div: %d\n", val);
you could use this:
int result;
getDivisorCounter(number, &result);
printf("amount of div: %d\n", result);
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've got a function whose inner code I want to convert into assembly (for various reasons):
int foo(int x, int y, int z);
I generated the assembly code using:
clang -S -mllvm --x86-asm-syntax=intel foo.c
The assembly output: foo.s starts off with something like:
_foo: ## #foo
.cfi_startproc
## BB#0:
push RBP
Ltmp2:
.cfi_def_cfa_offset 16
...
I assume this is the corresponding assembly code for that function. My question is, what part of the assembly output should I copy into the C code (I'm trying to use inline assembly) so that the function would work? The code should look like:
int foo(int x, int y, int z) {
__asm__("..."); // <-- What goes inside?
}
Thanks
You have to see the disassembly of that function and write the __asm__. For example below code
int foo(int x, int y, int z) {
x = y+z;
return x;
}
will yeild a disassembly of following :
int foo(int x, int y, int z) {
push ebp
mov ebp,esp
sub esp,0C0h
push ebx
push esi
push edi
lea edi,[ebp-0C0h]
mov ecx,30h
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]
x = y+z;
mov eax,dword ptr [y]
add eax,dword ptr [z]
mov dword ptr [x],eax
return x;
mov eax,dword ptr [x]
}
so you have to add below for statement x= y+z,
mov eax,dword ptr [y]
add eax,dword ptr [z]
mov dword ptr [x],eax