I never used _asm
nor -asm
(and I think the -asm
is invalid) instead I use this:
asm{
// multi line
// assembly code
// in here
};
This works without problems also in old turbo C++ and Pascal ... so simply get rid of all the -asm
and _asm
and just place single asm{
at start and }
at the end of your asm code.
However your code has many syntax errors !!!
for example what the heck is:
shl,eax,1
It should be:
shl eax,1
Or better, you can use a shl eax,16
instruction. Immediate shifts were new in 186, and code that uses 32-bit registers can only run on a 386 or newer. Or use 2x 16bit push
and 1x 32-bit pop
to move es
to higher 16 bit of eax
, as one way to concatenate two 16-bit things into a 32-bit register. (Fairly slow on modern CPUs, though.)
Another problem is that Turbo C++ compiler can not generate code with 32 bit registers, even in 16-bit mode (at least mine). So you'd really need to use a different compiler for any of that to be possible.
In the project option you can check x386 and x387 instruction set but that does not apply for inline assembly ... The lines with 32 bit registers will throw an error (unknown eax
or something like that) So you need to rewrite your code to use 16 bit registers.
Another problem is you use wrong hex number format.
In C/C++ you need to use 0x0
prefix instead of h
suffix. So for example 9000h
should be 0x09000
!!!
Another possible problem is addressing You use
add eax,ds[si]
You're using 16-bit pointers here, but later use a 32-bit [eax]
pointer. Are you sure that's what you want? Also, did you really want to do a 32-bit load, instead of just merging a new low 16 bits into EAX with mov ax, [si]
? The difference would be if a carry-out from the add propagated into the upper bits, or if the upper half of the dword at that memory location is non-zero.
Also, DS is the default segment (except for BP or ESP), so why specify DS explicitly here but not in [eax]
? You can't avoid the DS base with a 32-bit offset. Also the format is like this:
add ax,[ds:si]
or:
add eax,dword[ds:si]
Depending on what you want to do. The format of addressing is different among asm
compilers (TASM,NASM,...) which was a pain in the ass.
Also, asm mov ah,9
modifies the 2nd byte of EAX right before you try to dereference EAX as a pointer with mov al,[eax]
. Doing those two instructions in the other order could make sense, if you were constructing a valid offset (i.e. low part of a seg:off
address) in EAX.
And lastly but not least YOU DO NOT PRESERVE REGISTER VALUES that is simply a bad idea. Unlike MSVC where the compiler figures out which registers your asm uses and saves/restores them for you, you have to add push/pop
statements to start/end of your asm code otherwise you risk corrupting the register values the C/C++ compiler-generated code around your block was depending on.
Not all registers need to be preserved (unless you code ISR) but segment and index registers should be restored. IIRC ax holds the return value of asm functions (not sure if in TC too).
(In MSVC you can leave a value in EAX and then let execution fall off the end of a non-void function (without a return
), and the function's return value is what you left in EAX. MSVC actually supports this when inlining functions containing an asm{}
block, but if this works in TC it's probably just by not inlining and not messing up AX after asm blocks.)
Also take a look at this:
In the first bullets there are examples for TC++ using asm ...
Here another example (using x387) I just found in my TC archives:
long sin(float a,long b)
{
long alfa=long(a*1000);
long far* ptr=(long far*)0xA000FA00;
ptr[0]=alfa;
ptr[1]=1000;
ptr[2]=b;
asm {
fninit
push es
push di
mov di,0xA000
mov es,di
mov di,0xFA00
fild dword[es:di]
fild dword[es:di+4]
fdivp st(1),st(0)
db 0xD9,0xFE
fild dword[es:di+8]
fmulp st(1),st(0);
fistp dword[es:di]
pop di
pop es
}
b=ptr[0];
return b;
}
from which I infer the addressing syntax/format of TC as I simply did not remember anymore.