Close

Progress

A project log for BIOS adlib tracker

A really crude adlib tracker implemented as a PC BIOS.

michaelschrijvermichaelschrijver 12/30/2016 at 19:510 Comments

Turns out the keyboard code I wrote didn't really work as intended.

During christmas weekend I was using a different laptop to do some work on this project and I was quite surprised it would load fine in qemu, but the keyboard completely stopped working.

So I loaded it into bochs and this didn't work either, it loaded but no keyboard. I wasn't too surprised about bochs as I had done most testing in qemu so far.

So after diving into the cause I uncovered three problems:

- Bochs was using scancode set 1, qemu scancode set 2, I only implemented set 2

- I was not only reading the current scancode in the interrupt handler, but also the next scancodes for multibyte sequences. This worked fine in qemu, but bochs needs a delay for doing.

- The "christmas" laptop had a newer version of qemu. In this later version a kludge had been removed which used to support SeaBIOS. Apparently my code also relied on it.

The change in question is commit b8eb5512fd8a115f164edbbe897cdf8884920ccb, it removes initialization of the APIC, stopping it from delivering interrupts to the PIC.

-        s->lvt[APIC_LVT_LINT0] = 0x700;
At first I figured just adding this code to my tracker would fix the problem. No such luck, the base address of the APIC registers is out of reach for real-mode code.

However there's another option, disabling the APIC using an MSR. I added code doing that, checked qemu code to see if it should work (hw/intc/apic.c, function apic_check_pic_intr) and figured it should work. No such luck however.

Reading up a bit on the APIC I discovered it's possible to adjust the APIC base address, making it reachable again:

    mov     $0x1b, %cx                               
    # MSR for APIC
    
    rdmsr
    and     $~(((1 << 24) - 1) << 12), %eax    
    # mask out base offset
    or      $(8 << 12), %eax                   
    # remap physical base to 0x8000 aka %cs:0x7c00
    wrmsr
    mov     $0x700, %eax
    mov     %eax, %cs:(0x7c00 + 0x350)         
    # set LINT0 to ExtINT mode
So filled with hope I fired up qemu up again, and still it didn't work.

So while the first two were easy to fix, getting interrupts again from the PIC in newer qemu didn't work out for me. In the end I rewrote the code to poll the keyboard and timer. This is a poor way of implementing it, since it's quite wasteful of CPU cycles. But it did turn out shorter in terms of bytes, I ditched all the PIC initialization code and didn't need to set interrupt handlers, some new bytes were introduced to divide the PIT counter.

Now atleast the code is under 1024 bytes again, it works in recent versions of both qemu and bochs.

If anyone can enlighten me on where I went wrong, I'd be happy to hear :)

Next step is polishing the sound. There notes are currently off-by-three, I'd like a demotrack and it should sound like lasers instead of like beeps.

Discussions