Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using OUTB to set cursor position in my minimal OS kernel causes QEMU screen to flicker

I am getting started with a minimal OS kernel (just gdt and place holder idt). Using i386 assembly and freestanding C. I wanted to change the position of the cursor for which i found several sites giving the following.

void set_cursor_position(uint16_t row, uint16_t col) {
    uint16_t pos = row * 80 + col;

    // low byte
    outb(0x3D4, 0x0F);
    outb(0x3D5, pos & 0xFF);

    // high byte
    outb(0x3D4, 0x0E);
    outb(0x3D5, (pos >> 8) & 0xFF);
} 

Then for the outb function I found:

static inline void outb(uint16_t port, uint8_t val) {
    __asm__ volatile ("out %0, %1" : : "a"(val), "Nd"(port) : "memory");
}

However when i try to use the this the Qemu screen started flickering (the default text on the screen kept flickering). Just clearing the screen happens without a problem.

void clear_screen(void) {
    volatile char *vidptr = (char*)0xb8000;
    for (int i = 0; i < 80 * 25 * 2; i += 2) {
        vidptr[i] = ' ';
        vidptr[i + 1] = 0x07;  // Light gray on black
    }
}

Given the kernel main function:

void kernel_main(void) {
    init_idt();
    clear_screen();
    while(1);
}

Using GDB I am able to trace to the while(1), however now if I do:

void kernel_main(void) {
    init_idt();
    clear_screen();
    set_cursor_position(0, 0);
    while(1);
}

GDB can't seem to be able to even hit tbreak clear_screen, the bt giving some random address, further using info registers in Qemu I found that even the IDT base and limit aren't set properly (they were without this line I checked). It's not just the usage of set_cursor_position any line with outb called causes this for example:

void kernel_main(void) {
    init_idt();
    clear_screen();

    outb(0x3D4, 0x01);
    uint8_t width = inb(0x3D5) + 1;
    while(1);
}

The following are the build commands if they are of any help:

mkdir -p build
mkdir -p build/inter
nasm -f bin -I src/ src/bootloader.asm -o build/inter/bootloader.bin
nasm -f elf32 src/isr.asm -o build/inter/isr.o
gcc -m32 -ffreestanding -g -nostdlib -fno-stack-protector -O0 -fno-omit-frame-pointer -ffunction-sections -fdata-sections -Wall -Wextra -c src/idt.c -o build/inter/idt.o
nasm -f elf32 src/start_protected.asm -o build/inter/start_protected.o
gcc -m32 -ffreestanding -g -nostdlib -O0 -Wall -Wextra -c src/kernel.c -o build/inter/kernel.o
ld -m elf_i386 -T linker.ld -o build/inter/second_stage.elf build/inter/start_protected.o build/inter/kernel.o build/inter/idt.o build/inter/isr.o
ld: warning: build/inter/second_stage.elf has a LOAD segment with RWX permissions
objcopy -O binary build/inter/second_stage.elf build/inter/second_stage.bin
dd if=/dev/zero of=build/disk.img bs=512 count=2880
2880+0 records in
2880+0 records out
1474560 bytes (1.5 MB, 1.4 MiB) copied, 0.0238469 s, 61.8 MB/s
dd if=build/inter/bootloader.bin of=build/disk.img conv=notrunc
1+0 records in
1+0 records out
512 bytes copied, 0.000140471 s, 3.6 MB/s
dd if=build/inter/second_stage.bin of=build/disk.img bs=512 seek=1 conv=notrunc
1+1 records in
1+1 records out
992 bytes copied, 0.000108903 s, 9.1 MB/s
qemu-system-i386 -drive format=raw,file=build/disk.img

I am not sure how to fix this or even debug it. Any guidance is appreciated.

like image 449
Anonymous Avatar asked Jan 29 '26 06:01

Anonymous


1 Answers

I usually find bochs to be easier for debugging this low-level stuff, it's got a good integrated assembly language debugger and you can enable trace logging for the various emulated subsystems which could well give you a window into what the vga controller is seeing.

First, though, I'd start by using objdump to disassemble the code from your program - specifically the set_cursor_position function and calls through outb to check what exactly it has assembled. You need the port number in dx and the data in al, which it looks like you do.

I'd double check all of the port assignments - this is a good resource for starters: https://wiki.osdev.org/VGA_Hardware#Port_0x3C4,\_0x3CE,\_0x3D4

Specifically, 0x3D4 is an index register but it can be remapped to 0x3B4 depending on the overall state - you might want to check that. You might also want to check if there are any timing requirements - an idle time for the "hardware" to lock the values due to the CPU running faster than the bus, or if you need to set the bytes the other way around.

This page: http://www.osdever.net/FreeVGA/vga/crtcreg.htm gives some good background on the registers and this page http://www.osdever.net/FreeVGA/vga/textcur.htm gives an overview of the "quirks" to expect in the interface

Good luck!

like image 75
fredex42 Avatar answered Jan 31 '26 23:01

fredex42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!