Using an assembly function in a C program - c

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.

Related

Assembly retrieving buffer to c function parameter

I'm writing an assembly function that will read from IDE through ports.
I'm calling the parameters through x86 base pointer (EBP).
I debugged my kernel.bin (with gdb and qemu) and I that when I'm calling my recv buffer to print, eax will return values like:36h01h10h
IBM Char Table
My disk.asm is divided by read and write. Is it possible that I'm writing it wrong? Is it legal to move directly [ebp+16] to esi (to write)? If I, on read function, move [ebp+16] directly to edi is wrong? I'm using a register poiting to that address and making edi to point to that register:
In my disk.asm, to read the disk I have this:
sub dx, 7 ;dx = 0x1f0
mov ecx, 256
mov edi, bufferrecv
rep insw
(...)
push ebx
mov ebx, [ebp+16]
mov [ebx], long word bufferrecv
pop ebx
mov esp, ebp
pop ebp
ret
And to write disk:
sub dx, 7 ;dx = 0x1f0
mov ecx, 256
mov esi, [ebp+16]
rep outsw
(...)
I'm declaring those functions this way:
Kernel.c
extern int _readd(int sector_count, int nmrsector, STRING in_msg);
extern int writed(int sector_count, int nmrsector, STRING out_msg);
The STRING type was declared inside my types.h as char*

Initialize char[] fails, esi contains wrong value

I want to initialize a char array, but during I do this my programm crashes. Here's my code:
void kernelEnteredMsg() {
char str[] = "Kernel successfully entered!";
}
Here's the disassembly:
push ebp
mov ebp,esp
push edi
push esi
push ebx
sub esp,byte +0x30
lea edx,[ebp-0x2d]
mov ebx,0x402000 ; load an address outside my data segment
mov eax,0x1d
mov edi,edx
mov esi,ebx ; move this address to edi
mov ecx,eax
rep movsb ; here the programm crashes
add esp,byte +0x30
pop ebx
pop esi
pop edi
pop ebp
ret
I don't understand why it loads esi with 0x402000. But this seems to cause the error. Can somebody explain what happens here and how to fix it?
PS: "Kernel successful entered!" is at 0x1000 in binary file.
C code:
void kernelEnteredMsg();
void entryPoint() {
kernelEnteredMsg();
}
void kernelEnteredMsg() {
char str[] = "Kernel successfully entered!";
int size = 28;
}
Calling assembly code:
extern _entryPoint
global _main
section .text
_main: ; start of kernel
nop
; setup ds, es, ss and gs
mov ax, 16
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x4000
mov ax, 24
mov gs, ax
mov [gs:0], dword 0x07690748 ; test graphics
call _entryPoint ; enter kernel C code
jmp $
This code does copy the string from the .text section to the local stack, because the char array is not 'const'. This may provide a simple solution if you do not need the string to be modified - just make it const char.
I don't understand why it loads esi with 0x402000.
ESI is the source of the string copy instruction 'rep movsb', EDI is the destination.
The address is constructed by IMAGE_BASE+SECTION (IIRC) in the PE file(assuming it is PE.)
Remember in the file there is a FILE_ALIGN and a SECTION_VIRTUAL_ADDRESS, so a section may be
at position 0x1000 in the file(FILE_ALIGN) and at 0x2000 in memory(VIRTUAL_ADDRESS) resulting in IMAGE_BASE+VIRTUAL_ADDRESS=0x402000.
You can use a PE explorer like CFF Explorer(http://www.ntcore.com/exsuite.php)
to display this(if it's a .bin file it may be unapplicable but it has to have some kind of format)
Another possibility may be a wrong state of the DF-Flag leading to wrong behaviour of the string copy instruction (should not happen, because the compiler should take care of this).
Try inserting
__asm__ ("cld");
before the char str[] or in the __main procedure to set string increment to 'UP'.

Interpretation of C Code in IDA Pro Needed

I'm using IDA Pro to disassemble the following C code: However looking at the disassembly below it seems to me incomplete. The data is never initialized (as per C code) even though it does appear to be loaded into the stack however the procedure (nullsub_1) that is located at 00401040 makes no use of the data ? Am I making a correct assessment or am I missing something ??? I have used Visual C++ 6/2005 to compile the C code.
#include <stdio.h>
#include <windows.h>
struct a
{
char s[10];
BYTE b;
int i;
};
a al;
void init(a);
void main()
{
init(al);
};
void init(a c)
{
for(int j = 0; j < 10; j++) c.s[j] = 'A';
c.b = 10;
c.i = 10000;
};
.text:00401000 ; int __cdecl main(int argc,const char **argv,const char *envp)
.text:00401000 _main proc near ; CODE XREF: start+AFp
.text:00401000
.text:00401000 argc = dword ptr 4
.text:00401000 argv = dword ptr 8
.text:00401000 envp = dword ptr 0Ch
.text:00401000
.text:00401000 mov ecx, dword_4084C0
.text:00401006 mov edx, dword_4084C4
.text:0040100C sub esp, 10h
.text:0040100F mov eax, esp
.text:00401011 mov [eax], ecx
.text:00401013 mov ecx, dword_4084C8
.text:00401019 mov [eax+4], edx
.text:0040101C mov edx, dword_4084CC
.text:00401022 mov [eax+8], ecx
.text:00401025 mov [eax+0Ch], edx
.text:00401028 call nullsub_1
.text:0040102D add esp, 10h
.text:00401030 retn
.text:00401030 _main endp
.text:00401030
.text:00401030 ;
.text:00401031 align 10h
.text:00401040
.text:00401040
.text:00401040
.text:00401040 nullsub_1 proc near ; CODE XREF: _main+28p
.text:00401040 retn
.text:00401040 nullsub_1 endp
Your source code has no side effects other than just writing to memory. The compiler eliminates those writes as useless.
You may have better luck if you compile it in Debug mode (instead of Release) or turn off some compiler optimizations.
Alternatively, accesses to variables defined as volatile will be preserved, so you can add volatile in your code.

Embedded assembly in C (DOS) - illegal instruction

I'm trying to re-program instruction vector table. Here is the code I use:
#include <stdio.h>
int a=1;
void func();
void keyboard()
{
printf("\n\nkeyboard!!!\n");
a=0;
asm{iret}
}
int main ()
{
printf("starting...");
func();
return 0;
}
int vectorcs = 0;
int vectorip = 0;
void func()
{
printf("\n*****\n");
asm{
cli
mov ax,0
mov es,ax
mov bx,36
mov ax,word ptr es:[bx]
mov vectorip,ax
push ax
mov ax,word ptr es:[bx+2]
mov vectorcs,ax
push ax
mov ax,cs
mov word ptr es:[bx],offset keyboard
mov es:[bx+2],ax
sti
}
printf("\n%d %d\n",vectorip,vectorcs);
while (a) {
}
asm {
cli
mov es,bx
mov bx,36
pop ax
mov word ptr es:[bx+2],ax
}
asm{
pop ax
mov word ptr es:[bx],ax
sti
}
}
I'm using Turbo C++ 3.0
When I try to run this program, "16 Bit MS-DOS Subsystem: The NTVDM CPU has encountered an illegal instruction." appears. Then it shows contents of CS, OP, and IP registers. I can't continue the program. Any suggestions?
What you're doing is not right for multiple reasons:
Regular C functions can't be safely used as interrupt service routines because they don't correctly save, load and restore the CPU registers. They must be declared with the interrupt keyword. And they'll have iret for you at the end.
Variables that can change in the program asynchronously from interrupt routines must be declared as volatile, otherwise you're risking to have accesses to them incorrectly optimized out by the compiler.
Your inline assembly code probably corrupts the contents of CPU registers. One thing that's wrong with this code is that your asm blocks mess with the stack pointer. The first block exits with several extra words on the stack. This may be completely unexpected for the compiler and can break your program. There may be other issues, but I'm not going to check with the compiler documentation which registers must be preserved by inline assembly blocks. I'd avoid doing this altogether and opt for the setvect() function instead.
Calling most of standard library functions from inside of interrupt service routines is asking for trouble because these functions generally aren't reentrant/thread-safe. They can modify some global variables or states in completely unexpected ways for the rest of the program. The same is true for calling DOS service functions from the interrupt service routines (which your printf() relies on, btw). You can only call those when DOS says it's OK. It does so via the InDos flag variable and still, not all are safe to call when InDos=0.
See how to change interrupt vectors, define interrupt service routines and call DOS functions from them, all with Turbo C, in the answer to this question.
You may also find this question and its answers useful.
EDIT:
This is how you do it without dos.h's functionality with inline asm:
#include <stdio.h>
volatile int a = 1;
void interrupt (*pOldInt9)(void);
void func(void);
void interrupt keyboard(void)
{
printf("\n\nkeyboard!!!\n");
asm {
in al, 0x60
in al, 0x61
mov ah, al
or al, 0x80
out 0x61, al
mov al, ah
out 0x61, al
}
a = 0;
asm {
mov al, 0x20
out 0x20, al
}
}
int main(void)
{
printf("starting...");
func();
return 0;
}
void func(void)
{
printf("\n*****\n");
asm {
push bx
push es
mov bx, 9 * 4
mov ax, 0
mov es, ax
cli
mov ax, es:[bx]
mov word ptr pOldInt9, ax
mov word ptr es:[bx], offset keyboard
mov ax, es:[bx + 2]
mov word ptr pOldInt9[2], ax
mov es:[bx + 2], cs
sti
pop es
pop bx
}
while (a) {}
asm {
push bx
push es
mov bx, 9 * 4
mov ax, 0
mov es, ax
cli
mov ax, word ptr pOldInt9
mov es:[bx], ax
mov ax, word ptr pOldInt9[2]
mov es:[bx + 2], ax
sti
pop es
pop bx
}
}
asm {
cli
mov es,bx
mov bx,36
pop ax
mov word ptr es:[bx+2],ax
}
What does bx contain before that code?
void keyboard()
{
printf("\n\nkeyboard!!!\n");
a=0;
asm{iret}
}
This function has set up a stack frame that you haven't correctly destroyed.

Using assembly routines with C on DOS

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)

Resources