Any other way? INT abstraction

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Any other way? INT abstraction

Post 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 :D
Smilediver
Member
Member
Posts: 37
Joined: Sun Aug 05, 2007 4:23 pm

Post 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");
}
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post 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. :D
Korona
Member
Member
Posts: 1000
Joined: Thu May 17, 2007 1:27 pm
Contact:

Post by Korona »

You can use a macro:

Code: Select all

#define interrupt(num) asm ("int %0" : : "N" (num))
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post 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
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post 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
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post 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! :D
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

0xCC for int3 is only a convenience. The 'normal' form can still be used.
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post 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
Cemre
Member
Member
Posts: 31
Joined: Fri Nov 09, 2007 5:25 am

Post 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".
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post 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.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
zaleschiemilgabriel
Member
Member
Posts: 232
Joined: Mon Feb 04, 2008 3:58 am

Post by zaleschiemilgabriel »

I'm sorry, but I just have to say this: THIS IS HILARIOUS! :) Don't mind me! :roll:
DeviOuS - what a stupid name
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

Share your fun with us, will you?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
zaleschiemilgabriel
Member
Member
Posts: 232
Joined: Mon Feb 04, 2008 3:58 am

Post 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.
DeviOuS - what a stupid name
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post 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.
Post Reply