Page 1 of 2
Any other way? INT abstraction
Posted: Fri Sep 07, 2007 12:08 am
by neon
Hey everyone,
I am abstracting certain assembly language routines that C++ is unable to do behind an interface that is non portable.
One of these is the common INT instruction.
What I am trying to do is develope a small inline routine to generate an arbitrary interrupt:
Code: Select all
EXTERN inline void CALLING geninterrupt (int interruptnum) {
_asm {
// How should I execute the INT instruction?
}
}
The problem is that INT can only accept a constant literal as an argument. So, how am I supposed to use a variable or register? I can't.
It seems the only syntax that is acceptable is
INT value which will not work here.
The only option I can think of is hard coding each INT call (i.e., Perhaps a large switch), which is very ugly.
Any suggestions are appreciated
Posted: Fri Sep 07, 2007 1:07 am
by Smilediver
I doubt you'll need a lot of these, so you can abstract it even more. Something like this:
Code: Select all
void DoDebugInterrupt()
{
asm("int 0x3");
}
Posted: Fri Sep 07, 2007 1:44 am
by neon
Thanks for the reply.
I personally rather not do that though.
The only reason I am abstracting these routines is portability amongst assemblers, and processor instruction sets. I am only abstracting the instructions that requires inline assembly to use, so most of the Kernel does not need to. This also makes things easier to modify in this way.
The use of each interrupt is implementation defined; and thus abstracting each interrupt (like what you posted) will not be a good idea right now.
Considering the routine is inline, The switch should not waist that much clock cycles. At least I hope not...
I am actually trying to reimplement the geninterrupt() routine that Turbo C uses within their <dos.h> header file.
I still cannot seem to find any alternative method though.
Any other suggestions are appreciated.
Thanks.
Posted: Fri Sep 07, 2007 2:22 am
by Korona
You can use a macro:
Code: Select all
#define interrupt(num) asm ("int %0" : : "N" (num))
Posted: Fri Sep 07, 2007 2:37 am
by neon
A macro was one of the first things I tried.
Heres the macro:
Code: Select all
#define interrupt(num) _asm int num;
The current routine:
Code: Select all
EXTERN inline void CALLING geninterrupt (int interruptnum) {
interrupt (0);
}
It works just fine if I pass in explicate values (Like the 0 above), however
any varable failes to work, of
any size.
I always recieve the following error when I do:
Code: Select all
EXTERN inline void CALLING geninterrupt (int interruptnum) {
interrupt (interruptnum);
}
Error:
Code: Select all
error C2415: improper operand type
Posted: Fri Sep 07, 2007 3:01 am
by Combuster
that is because the INT instruction only takes constants, so you can't use variables normally.
One way around it can be achieved by resolving to self-modifying code and writing a bit of ugly assembly: (untested)
Code: Select all
interrupt:
mov AL, [ESP+4]
mov [.hack+1], AL
jmp .hack
.hack:
int 0
ret
Posted: Fri Sep 07, 2007 3:32 am
by neon
That would work considering the INT number is encoded in the second byte of the OPCode. I would need to watch for INT 03 though, so that I can generate the proper 0xCC debug INT 3 instruction.
Perhaps I should just favor nicer code, and use a large switch statement instead.
Any other suggestions are appreciated
Actually, I am going to try your method first for now.
I wont be able to tell you if it works until I set up my IDT
Thanks!
Posted: Fri Sep 07, 2007 3:50 am
by JamesM
0xCC for int3 is only a convenience. The 'normal' form can still be used.
Posted: Tue Mar 11, 2008 3:05 am
by devel
Hi,
I am just asking you whether this self-modifying code will also work in virtual-8086 mode.
I did arbitrary interrupt generation that way in real mode but I am not sure about second
mode . I guess it works. In my case interrupt generation relates to 16-bit bios's services
so I will not use that in other modes.
Anyway I am still not sure that using self-modifying code is used legally in my case.
regards,
devel
Posted: Tue Mar 11, 2008 3:26 am
by Cemre
although it will work, I wouldn't suggest using self-modifying code. it will really slow down the CPU because it will flush the entire instruction prefetch queue.
you can use lapic ( local apic ) for generating a self interrupt, and this way you can use a variable for "intnum".
Posted: Tue Mar 11, 2008 5:07 am
by Combuster
it will really slow down the CPU because it will flush the entire instruction prefetch queue.
older processors also have a prefetch queue. Instructions that have been decoded but not executed are not updated when modifying code a few bytes ahead. On those processors you'll need the flush to have them work consistently.
nevertheless, the pipeline flush'll also be there if you use a jump table and 256 int opcodes.
I am just asking you whether this self-modifying code will also work in virtual-8086 mode.
As said, untested. However, if it works in real mode, then it will do as well in any other processor mode. You should only be careful with the fact that an INT in v8086 mode might trap to the monitor rather than executing directly.
Posted: Tue Mar 11, 2008 5:27 am
by zaleschiemilgabriel
I'm sorry, but I just have to say this: THIS IS HILARIOUS!
Don't mind me!
Posted: Tue Mar 11, 2008 5:56 am
by Combuster
Share your fun with us, will you?
Posted: Tue Mar 11, 2008 6:12 am
by zaleschiemilgabriel
Well, C was originally written in assembly language, and now, for OS development people are trying to "emulate" assembly-like instructions in C. That's funny.
Posted: Tue Mar 11, 2008 7:37 am
by devel
I think I will rather leave this self-modifying implementation as "Architectures Optimization
Reference Manual" also suggests to avoid wherever possible. Well I've decided to have
hardcoded most 'popular' bios services and create macro that can define the rest.