3

I'm trying to set up a hardware interrupt handler in protected mode, using for compiling in . Here's the smallest code possible (timer interrupt), I guess:

#include <dpmi.h>
#include <go32.h>
#include <stdio.h>

unsigned int counter = 0;

void handler(void) {
    ++counter;
}
void endHandler(void) {}

int main(void) {
    _go32_dpmi_seginfo oldInfo, newInfo;

    _go32_dpmi_lock_data(&counter, sizeof(counter));
    _go32_dpmi_lock_code(handler, endHandler - handler);

    _go32_dpmi_get_protected_mode_interrupt_vector(8, &oldInfo);

    newInfo.pm_offset = (int) handler;
    newInfo.pm_selector = _go32_my_cs();
    _go32_dpmi_allocate_iret_wrapper(&newInfo);

    _go32_dpmi_set_protected_mode_interrupt_vector(8, &newInfo);

    while (counter < 3) {
        printf("%u\n", counter);
    }

    _go32_dpmi_set_protected_mode_interrupt_vector(8, &oldInfo);
    _go32_dpmi_free_iret_wrapper(&newInfo);

    return 0;
}

Note that I'm not chaining my handler but replacing it. The counter won't increase beyond 1 (therefore never stopping the main loop) making me guess that the handler doesn't return correctly or is called only once. Chaining on the other hand works fine (remove the wrapper-lines and replace set_protected_mode with chain_protected_mode). Am I missing a line?

tobywoby
  • 85
  • 5
  • 2
    `counter` should be marked `volatile`. – Jonathon Reinhart Feb 08 '17 at 11:12
  • Also, get rid of the unnecessary casts in the calls to `_go32_dpmi_lock_code` and `...data`. They take `void*` (which will accept a pointer of any type) and `size_t` (which is the result of `sizeof`). – Jonathon Reinhart Feb 08 '17 at 11:15
  • 1
    Also, your use of `endHandler` to try and calculate the size of `handler` is dubious. Nothing guarantees that `endHandler` will come immediately after `handler` as you seem to expect. In GCC, the better solution is to put `handler` in a custom section and use a linker script variable to get its size. I'm not sure about DJGPP however. – Jonathon Reinhart Feb 08 '17 at 11:19
  • Did you try starting with the [example in the documentation](http://www.delorie.com/djgpp/doc/libc/libc_446.html)? – Jonathon Reinhart Feb 08 '17 at 11:22
  • I did not start with your linked example but with [this guide](http://www.delorie.com/djgpp/doc/ug/interrupts/overview.html) and looked into [this](http://www.delorie.com/djgpp/doc/ug/interrupts/inthandlers2.html), which are on the DJGPP homepage so I thought they'd be fine (they get the handler's size the same way). Using `volatile`'s a first for me. Finally I read [this tutorial](https://soulsphere.org/random/old-dos-code/docs/iea292.txt) though its code seems to be incorrect in small parts. And you're right, I will do away with the kinda obscuring casts. – tobywoby Feb 08 '17 at 12:05

1 Answers1

0

You need to chain the old interrupt handler, like in the example Jonathon Reinhart linked to in the documentation, as the old handler will tell the interrupt controller to stop asserting the interrupt. It will also have the added benefit of keeping the BIOS clock ticking, so it doesn't lose a few seconds each time you run the program. Otherwise when your interrupt handler returns the CPU will immediately call the handler again and your program will get stuck in an infinite loop.

Also there's no guarantee that GCC will place endHandler after handler. I'd recommend just simply locking both the page handler starts on and the next page in case it straddles a page:

_go32_dpmi_lock_code((void *) handler, 4096);

Note the cast is required here, as there's no automatic conversion from pointer to a function types to pointer to void.

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • Thanks! The info about the old handler being _that_ important is nice to have. I looked into it further and found out [what the old handler probably does.](http://webpages.charter.net/danrollins/techhelp/0105.HTM) Adding `outportb(0x20, 0x20);` to my handler seems to make it work like the old one. But I will leave it at chaining if it really has that added benefit you mentioned. – tobywoby Feb 08 '17 at 22:25
  • 1
    @tobywoby It might not lose time on DOSBox which might not fully emulate the BIOS clock, for example by using the host operating system clock instead. On a real PC though it will lose time because the BIOS's timer interrupt handler increments a counter used by the BIOS to keep track of the current time. – Ross Ridge Feb 09 '17 at 03:20