Page 1 of 2
Tip: Reducing the repetitive asm code from Bran's tutorial
Posted: Sun Sep 30, 2007 9:14 am
by Craze Frog
There is a good tutorial that walks you through writing a kernel here:
http://www.osdever.net/bkerndev/index.php
However, you're expected to write a lot of similar functions:
Code: Select all
global _isr0
global _isr1
global _isr2
... ; Fill in the rest here!
global _isr30
global _isr31
; 0: Divide By Zero Exception
_isr0:
cli
push byte 0 ; A normal ISR stub that pops a dummy error code to keep a
; uniform stack frame
push byte 0
jmp isr_common_stub
... ; Fill in from 2 to 7 here!
; 8: Double Fault Exception (With Error Code!)
_isr8:
cli
push byte 8 ; Note that we DON'T push a value on the stack in this one!
; It pushes one already! Use this type of stub for exceptions
; that pop error codes!
jmp isr_common_stub
... ; You should fill in from _isr9 to _isr31 here. Remember to
; use the correct stubs to handle error codes and push dummies!
Using a simple fasm macro you can avoid that, and write the code once. This makes your program less vulnerable to typing errors.
I made two different macros, one to generate code for the ISRs that need to push a dummy error code and one for the ISRs where the processor pushes the error code.
Code: Select all
macro ISR_AUTOERR [NUM] {
public isr#NUM
isr#NUM:
cli
push 0
push NUM
jmp isr_common_stub
}
macro ISR_CPUERR [NUM] {
public isr#NUM
isr#NUM:
cli
push NUM
jmp isr_common_stub
}
; This works the same as calling the macro many times with
; a different number as the parameter each time.
; Reason for this is the [] around NUM in the macro declaration
ISR_AUTOERR 0, 1, 2, 3, 4, 5, 6, 7
ISR_CPUERR 8
ISR_AUTOERR 9
ISR_CPUERR 10, 11, 12, 13, 14
ISR_AUTOERR 15, 16, 17, 18, 19, 20, 21, 22, 23, 24
ISR_AUTOERR 25, 26, 27, 28, 29, 30, 31
A similar macro can of course also be used for the IRQ handlers.
Posted: Sun Sep 30, 2007 9:37 am
by Brynet-Inc
It just makes sense using GAS or NASM, believe it or not... some people actually like the ability of cross-compiling their x86 OS on a PowerPC Mac..
Anyway, the last time I talked to Brandon, he didn't seem very interested in OSDev anymore...
Posted: Sun Sep 30, 2007 1:15 pm
by Dex
Brynet-Inc wrote:It just makes sense using GAS or NASM, believe it or not... some people actually like the ability of cross-compiling their x86 OS on a PowerPC Mac..
Anyway, the last time I talked to Brandon, he didn't seem very interested in OSDev anymore...
But you can just as easy say:
"it just makes sense using FASM, believe it or not... some people actually like the ability to port there assembler to there OS"
Posted: Sun Sep 30, 2007 1:26 pm
by elderK
If you actually felt like it, you could read about macros in GAS and do the exact same thing ...
~Z
Posted: Sun Sep 30, 2007 1:52 pm
by JamesM
... Or you could read my tutorials and find the NASM version of those macros...
Posted: Thu Oct 04, 2007 7:09 am
by Craze Frog
JamesM wrote:... Or you could read my tutorials and find the NASM version of those macros...
How would you make your nasm macros take multiple parameters?
Posted: Thu Oct 04, 2007 7:14 am
by JamesM
Read tutorial 5 and find out!
Posted: Thu Oct 04, 2007 8:21 am
by os64dev
zeii wrote:If you actually felt like it, you could read about macros in GAS and do the exact same thing ...
~Z
done it already for my os.
Code: Select all
.text
.code64
.macro function name
.align 0x10;
.globl \name;
.type \name, @function;
\name:
.endm
.macro vector1 number
.align 0x10;
pushq %rdi;
movq $(_ZN3x6420interruptVectorTableE + (\number * 8)), %rdi;
jmp interruptVectorStage2;
.endm
.macro vector1List from, to
.align 0x10;
vector1 \from;
.if \to - \from
vector1List "(\from+1)", \to
.endif
.endm
.macro vector2 number
.align 0x10;
pushq %rdi; //- 1 Byte opcode for dummy error code.
pushq %rdi;
movq $(_ZN3x6420interruptVectorTableE + (\number * 8)), %rdi;
jmp interruptVectorStage2;
.endm
.macro vector2List from, to
.align 0x10;
vector2 \from;
.if \to - \from
vector2List "(\from+1)", \to
.endif
.endm
function interruptVectorBase
vector2List 0, 7
vector1 8
vector2 9 //- reserved
vector1List 10, 14
vector2List 15, 16 //- reserved
vector1 17
vector2List 18, 63
vector2List 64, 127
vector2List 128, 191
vector2List 192, 255
//function interruptVectorStage2
.align 0x10;
interruptVectorStage2:
//- stackframe at this point:
//- %SS | %RSP | %RFL | %CS | %RIP | ERR | %RDI
//- %RDI : contains vector address.
//- save general purpose registers.
pushq %rsi;
pushq %rax;
pushq %rbx;
pushq %rcx;
pushq %rdx;
pushq %rbp;
pushq %r8;
pushq %r9;
pushq %r10;
pushq %r11;
pushq %r12;
pushq %r13;
pushq %r14;
pushq %r15;
pushw %fs;
pushw %gs;
//- call the interrupt handler (vector).
movq %rsp, %rsi;
call *(%rdi);
//- restore general purpose registers.
popw %gs;
popw %fs;
popq %r15;
popq %r14;
popq %r13;
popq %r12;
popq %r11;
popq %r10;
popq %r9;
popq %r8;
popq %rbp;
popq %rdx;
popq %rcx;
popq %rbx;
popq %rax;
popq %rsi;
popq %rdi;
addq $0x08, %rsp;
iretq;
Posted: Thu Oct 04, 2007 10:45 am
by Craze Frog
JamesM wrote:Read tutorial 5 and find out!
I mean how to make them behave like this:
Code: Select all
ISR_AUTOERR 0, 1, 2, 3, 4, 5, 6, 7
ISR_CPUERR 8
ISR_AUTOERR 9
Your code requires this:
Code: Select all
ISR_AUTOERR 0
ISR_AUTOERR 1
ISR_AUTOERR 2
ISR_AUTOERR 3
ISR_AUTOERR 4
ISR_AUTOERR 5
ISR_AUTOERR 6
ISR_AUTOERR 7
ISR_CPUERR 8
ISR_AUTOERR 9
Posted: Thu Oct 04, 2007 11:03 am
by JamesM
Well, that can't be done with the NASM macro mechanism.
Posted: Thu Oct 04, 2007 12:01 pm
by Craze Frog
I think it can, using %rep and %rotate, but I don't know how.
Posted: Thu Oct 04, 2007 2:02 pm
by neon
I personally do not like the setup either way. It works, but is very repetitve no matter how one looks at it. With that, it requires a common IRQ stub function to bring everything together, requiring the testing of individual interrupt numbers to determine what to do.
Would it not be better to register function callbacks inside of the IDT when needed? This not only will take out all of the repetitive functions, but will separate the hardware dependency (The x86 specific IDT) from the kernel. Assuming the HAL is a separate dynamic library, this will work great. I am actually looking for comments on this setup
Let me describe my idea a little more...
Inside of the HAL's initialization routine, the HAL maps the IDT, and registers a single stub routine for each entry to trap kernel errors. Upon returning control back to the Kernel, The Kernel can later register callback functions through the HAL to register its own interrupt handlers.
The HAL's stub routine only halts the processor and ends execution--no more. As the Kernel has to go through the HAL in order to register callbacks, the IDT is completely independent of the Kernel.
This is both non repetitive, and very portable, as the IDT is seperate from the kernel. And for interrupts that the Kernel has not registered, the HAL's stub routine tkes control.
What do you think?
Posted: Thu Oct 04, 2007 2:14 pm
by JamesM
Would it not be better to register function callbacks inside of the IDT when needed?
But... But... how would you know which interrupt was called? And what would happen if a particular interrupt fired that was "unhandled"?
Posted: Thu Oct 04, 2007 2:36 pm
by neon
But... But... how would you know which interrupt was called? And what would happen if a particular interrupt fired that was "unhandled"?
Unhanded interrupts would be handled by the HAL:
Inside of the HAL's initialization routine, the HAL maps the IDT, and registers a single stub routine for each entry to trap kernel errors.
As for which interrupt was called...Hm... I'll get back on that one :/ While we can easily provide a way of deciding interrupt numbers when registering each interrupt handler, the interrupt numbers themselves are x86 dependent. I guess we will need to find a way to abstract the numbers is some way... Perhaps behind constants defined within the HAL?
So, it would be something simular to:
Code: Select all
LoadLibrary ("hal.dll"); // load dynamic library
void (*Initialize) () = GetProcAddress ("Initialize");
// Initialize HAL. Initialize default chipset configuration for x86 specific
// motherboards
(*Initialize) ();
// enable interrupts. HAL has set up default stub interrupt handler
sti();
// register whatever callbacks we want to handle hardware faults:
void (*RegisterHandler)(LPHANDLER, int) = GetProcAddress ("RegisterHandler");
// Register our interrupt handler. DivideHandler is the interrupt handler,
// ID_DIVIDE_BY_0 is a special name given in the HAL meaning
// divide by 0 interrupt handler (interrupt 0)
(*RegisterHandler) (DivideHandler, ID_DIVIDE_BY_0);
The handler might look something like this:
Code: Select all
int __cdecl DivideHandler (REGS* regs, int lparam, int rparam, int code) {
// regs contains the register state at the time of the interrupt.
// lparam and rparam contains extra data, if any.
// code contains the error code. The HAL should have symbolic names
// for the error codes.
return HANDLER_CONTINUE; // continue execution after handler
}
Posted: Thu Oct 04, 2007 2:48 pm
by frank
What about shared interrupts? Of course when asking this I am thinking you are patching the IDT with the ISR for the driver and not a middle man function that then signals the driver. If you mean the middle man thing then ignore this post.