cosdevdouble-buffering

OSDev -- double buffering rebooting system


Hello I'm trying to make a simple os, and I'm currently trying to do double buffering.
I have two arrays in the size of the screen and a function that copy the array that currently is being used to the memory of the screen and then swap the being used array to the second one.
And there is a function that clear the screen and set it to a specific color.

screen.c

static u8 *BUFFER = (u8*) 0xA0000;

u8 buffers[2][SCREEN_SIZE];
u8 current_buffer = 0;

#define CURRENT     (buffers[current_buffer])
#define SWAP()      (current_buffer = 1 - current_buffer)

void screen_swap(){
    memcpy(BUFFER, CURRENT, SCREEN_SIZE);
    SWAP();
}

void clear_screen(u8 color){                  // set all the memory of the screen to one color
    memset(&CURRENT, color, SCREEN_SIZE);    
}

memory.c

void memset(void* src, u8 val, u32 len){
    u8* ptr = (u8*)src;
    while(len--){
        *ptr++ = val;
    }
}

void* memcpy(void* dst, void* src, u32 len){
    u8 *d = (u8*)dst;
    const u8 *s = (const u8*)src;
    
    while (len-- > 0){
        *d++ = *s++;
    }
    return dst;
}

When I try to run these functions the system keeps rebooting. For example:

    clear_screen(COLOR(0,0,255));
    screen_swap();

A link to my github repo for more context


Solution

  • The TL;DR is that you are blasting your program stack.

    I added some debug printf calls using a macro: dbgprt

    1. stack address is 0x7be8 (upon entering kernel main)
    2. &buffers[0][0] is 0x5f60
    3. SCREEN_SIZE is 0xfa00
    4. Address range of the low buffers is 0x5f60-0x1f960
    5. When we do a memset on buffers, we write well beyond the address of the stack.

    The stack address in question (i.e. 0x7be8) is the stack address used during the boot process. In main.asm, this is set with:

        mov sp, 0x7C00              ; stack grows downwards from where we are loaded in memory
    

    At jmpToKernel, we need to set the esp register to a value that doesn't conflict with anything (e.g.):

        mov esp,0x90000
    

    Here is the [redacted] output of make run:

    # qemu-system-i386 -serial file:serial.log -fda build/os-image.img
    ###qemu-system-i386 -serial stdio -fda build/os-image.img
    qemu-system-i386 -serial stdio -fda build/os-image.img
    WARNING: Image format was not specified for 'build/os-image.img' and probing guessed raw.
             Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
             Specify the 'raw' format explicitly to remove the restrictions.
    Unhandled IRQ 0
    
    set_pallette: ENTER
    set_pallette: EXIT
    SCREEN_SIZE=fa00
    STACK=7be8
    screen_swap: ENTER
    screen_swap: BUFFER=0xa0000 current_buffer=0/0x5f60/0x15960
    screen_swap: EXIT
    clear_screen: ENTER color=0x1c
    clear_screen: BUFFER=0xa0000 current_buffer=1/0x15960/0x25360
    clear_screen: EXIT
    screen_swap: ENTER
    screen_swap: BUFFER=0xa0000 current_buffer=1/0x15960/0x25360
    screen_swap: EXIT
    clear_screen: ENTER color=0x1c
    clear_screen: BUFFER=0xa0000 current_buffer=0/0x5f60/0x15960
    qemu-system-i386: Trying to execute code outside RAM or ROM at 0x1c1c1c1c
    This usually means one of the following happened:
    
    (1) You told QEMU to execute a kernel for the wrong machine type, and it crashed on startup (eg trying to run a raspberry pi kernel on a versatilepb QEMU machine)
    (2) You didn't give QEMU a kernel or BIOS filename at all, and QEMU executed a ROM full of no-op instructions until it fell off the end
    (3) Your guest kernel has a bug and crashed by jumping off into nowhere
    
    This is almost always one of the first two, so check your command line and that you are using the right type of kernel for this machine.
    If you think option (3) is likely then you can try debugging your guest with the -d debug options; in particular -d guest_errors will cause the log to include a dump of the guest register state at this point.
    
    Execution cannot continue; stopping here.
    
    make: *** [Makefile:37: run] Error 1
    

    Here is a git diff of what I changed:

    Edit: Removed this for space. See update below.


    UPDATE:

    I added mov esp, 0x90000, and now I can only write to the second buffer -> buffers[1]. Is there a different place in memory I can put the stack in or how can I store the buffer not in the stack? – neta cohen

    Of course. I just grabbed that from my other "build your own OS" answer: How can I fix my VBE implementation for my OS?

    That was a bit of a hack [and what the original author was doing].

    At first, I was going to use the make process to define the address for boot.asm. Then, I realized a better way is to have the _start in kernel_entry.asm set it.

    1. The usual way would be to probe memory and get its size. Then, put the stack pointer pointing to the last available word in RAM.
    2. But, probing in that way and catching the protection exception or bus error would be a bit tricky to implement at this stage.
    3. So, using a static array, defined in kernel_entry.asm is the simplest way to go.

    While in the process of doing that, I was still having some trouble, so I changed some unrelated things to improve debugging:

    1. Changed the printf-like function to allow (e.g. %d, %ld, and %lld) to support int/long/long long numbers (so I could print the timer value).
    2. Changed printf to use stdarg.h
    3. Added "debounce" code to main so that it only changed the background color on initial key press (e.g. rising edge).

    Here is the "no crash" version of the code:

    diff --git a/Makefile b/Makefile
    index 193cb35..e4cb5fa 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -1,17 +1,24 @@
    +# tetos/Makefile -- make file for tetos
    +#
    +# SO: osdev double buffering rebooting system
    +# SITE: stackoverflow.com
    +# SO: 77524100
    +include ./common.mk
     ASM=nasm
    -CC=/home/neta/opt/cross/bin/i386-elf-gcc
    -LD=/home/neta/opt/cross/i386-elf/bin/ld
     RM=rm
     OBJCOPY=objcopy
    -
     SRC=src
    -BUILD=build
    +export BUILD=build
    +
    +IMGCOUNT = 2880
    +KNLCOUNT = 2048
    
    +all: $(BUILD)/os-image.img
    
     $(BUILD)/os-image.img: boot kernel
    -   dd if=/dev/zero of=$@ bs=512 count=2880
    +   dd if=/dev/zero of=$@ bs=512 count=$(IMGCOUNT)
        dd if=$(BUILD)/boot.bin of=$@ conv=notrunc bs=512 count=1 seek=0
    -   dd if=$(BUILD)/kernel.bin of=$@ conv=notrunc bs=512 count=2048 seek=1
    +   dd if=$(BUILD)/kernel.bin of=$@ conv=notrunc bs=512 count=$(KNLCOUNT) seek=1
    
     boot: $(BUILD)/boot.bin
    @@ -31,6 +38,7 @@ always:
    
     run: $(BUILD)/os-image.img
        # qemu-system-i386 -serial file:serial.log -fda $<
    +   ###qemu-system-i386 -serial stdio -fda $<
        qemu-system-i386 -serial stdio -fda $<
    
     debug: $(BUILD)/os-image.img
    @@ -38,6 +46,7 @@ debug: $(BUILD)/os-image.img
        # for logs -- log: bochslog.txt
    
     clean:
    -   @$(MAKE) -C $(SRC)/boot BUILD=$(abspath $(BUILD)) clean
    -   @$(MAKE) -C $(SRC)/kernel BUILD=$(abspath $(BUILD)) clean
    +   rm -fr $(BUILD)
    +   @###$(MAKE) -C $(SRC)/boot BUILD=$(abspath $(BUILD)) clean
    +   @###$(MAKE) -C $(SRC)/kernel BUILD=$(abspath $(BUILD)) clean
    
    diff --git a/common.mk b/common.mk
    new file mode 100644
    index 0000000..f0f2b8b
    --- /dev/null
    +++ b/common.mk
    @@ -0,0 +1,18 @@
    +# common.mk -- common definitions for make
    +
    +CC:=/home/neta/opt/cross/bin/i386-elf-gcc
    +LD:=/home/neta/opt/cross/i386-elf/bin/ld
    +
    +ifeq ($(wildcard $(CC)*),)
    +  CC:=gcc -m32
    +  LD:=ld -melf_i386
    +endif
    +
    +export CC
    +export LD
    +
    +IMGCOUNT = 2880
    +KNLCOUNT = 2048
    +
    +###OBJ := obj
    +OBJ := o
    diff --git a/src/boot/Makefile b/src/boot/Makefile
    index 4c9a266..c6d6f8d 100644
    --- a/src/boot/Makefile
    +++ b/src/boot/Makefile
    @@ -1,12 +1,15 @@
    +include ../../common.mk
     BUILD?=build/
     ASM?=nasm
    
    +###AFLAGS += -DKSTACK_SECTOR=$(KNLCOUNT)
    +
     .PHONY: all clean
    
     all: $(BUILD)/boot.bin
    
     $(BUILD)/boot.bin: main.asm
    -   $(ASM) main.asm -f bin -o $(BUILD)/boot.bin
    +   $(ASM) main.asm -f bin -o $(BUILD)/boot.bin -l $(BUILD)/boot.lst $(AFLAGS)
    
     clean:
        rm -f $(BUILD)/boot.bin
    diff --git a/src/boot/main.asm b/src/boot/main.asm
    index 9a37335..8cc8d06 100644
    --- a/src/boot/main.asm
    +++ b/src/boot/main.asm
    @@ -51,8 +51,7 @@ jmpToKernel:
         mov ds, ax
         mov ss, ax
    
    -    call KERNEL_OFFSET
    -    hlt
    +    jmp KERNEL_OFFSET
    
     bits 16
     EnableA20:
    diff --git a/src/kernel/Makefile b/src/kernel/Makefile
    index 7bf9326..1e1f7a4 100644
    --- a/src/kernel/Makefile
    +++ b/src/kernel/Makefile
    @@ -1,38 +1,43 @@
    +include ../../common.mk
     BUILD?=build/
     ASM?=nasm
     ASMFLAGS?=-f elf
    -CC?=/home/neta/opt/cross/bin/i386-elf-gcc
    -LD=/home/neta/opt/cross/bin/i386-elf-ld
    -LGCC=/home/neta/opt/cross/lib/gcc/i386-elf/13.1.0
    +###CC?=/home/neta/opt/cross/bin/i386-elf-gcc
    +###LD=/home/neta/opt/cross/bin/i386-elf-ld
    +###LGCC=-L /home/neta/opt/cross/lib/gcc/i386-elf/13.1.0
     # CCFLAGS= -ffreestanding -nostdlib -c
    -CCFLAGS=-m32 -std=c11 -O2 -g -Wall -Wextra -Wpedantic -Wstrict-aliasing
    +CCFLAGS+=-m32 -std=c11 -O2 -g -Wall -Wextra
    +CCFLAGS+=-Werror
    +###CCFLAGS+=-Wpedantic -Wstrict-aliasing
     CCFLAGS+=-Wno-pointer-arith -Wno-unused-parameter
    -CCFLAGS+=-nostdlib -nostdinc -ffreestanding -fno-pie -fno-stack-protector
    +CCFLAGS+=-nostdlib -ffreestanding -fno-pie -fno-stack-protector
    +###CCFLAGS+=-nostdinc
     CCFLAGS+=-fno-builtin-function -fno-builtin -c -mgeneral-regs-only
    -LDFLAGS=-ffreestanding -O2 -nostdlib -m32 -L $(LGCC) -lgcc
    +LDFLAGS=-ffreestanding -O2 -nostdlib -m32 $(LGCC) -lgcc
    
     SOURCES_C=$(wildcard *.c)
     SOURCES_ASM=$(wildcard *.asm)
    -OBJECTS_C=$(patsubst %.c,$(BUILD)/kernel/c/%.obj,$(SOURCES_C))
    -OBJECTS_ASM=$(patsubst %.asm,$(BUILD)/kernel/asm/%.obj,$(SOURCES_ASM))
    +OBJECTS_C=$(patsubst %.c,$(BUILD)/kernel/c/%.$(OBJ),$(SOURCES_C))
    +OBJECTS_ASM=$(patsubst %.asm,$(BUILD)/kernel/asm/%.$(OBJ),$(SOURCES_ASM))
    
     .PHONY: all clean always
    
    -all: kernel.bin
    +all: always kernel.bin
    
     kernel.bin: $(OBJECTS_ASM) $(OBJECTS_C)
    -   $(CC) -T link.ld -o $(BUILD)/kernel.bin $^ $(LDFLAGS)
    -   # $(LD) -o $(BUILD)/kernel.bin $(LDFLAGS) -Tlink.ld $^
    +   @###$(CC) -T link.ld -o $(BUILD)/kernel.bin $^ $(LDFLAGS)
    +   $(CC) -Wl,-Map=$(BUILD)/kernel.map -T link.ld -o $(BUILD)/kernel.bin $^ $(LDFLAGS)
    +   @# $(LD) -o $(BUILD)/kernel.bin $(LDFLAGS) -Tlink.ld $^
    
    -$(BUILD)/kernel/c/%.obj: %.c always
    +$(BUILD)/kernel/c/%.$(OBJ): %.c ###always
        $(CC) $(CCFLAGS) -o $@ $<
    
    -$(BUILD)/kernel/asm/%.obj: %.asm always
    -   $(ASM) $(ASMFLAGS) -o $@ $<
    +$(BUILD)/kernel/asm/%.$(OBJ): %.asm ###always
    +   $(ASM) $(ASMFLAGS) -o $@ $< -l $(basename $@).lst
    
     always:
        mkdir -p $(BUILD)/kernel/c
        mkdir -p $(BUILD)/kernel/asm
    
     clean:
    -   rm -f $(BUILD)/kernel.bin
    +   rm -f $(BUILD)/kernel.bin $(BUILD)/kernel.map
    diff --git a/src/kernel/hal.c b/src/kernel/hal.c
    index 1b5a7b1..433447c 100644
    --- a/src/kernel/hal.c
    +++ b/src/kernel/hal.c
    @@ -7,13 +7,15 @@
     #include "keyboard.h"
    
     void HAL_init(){
    +    init_serial();              // init the COM1 port (for qemu and bochs)
    +
         IDT_init();                 // init the IDT
         ISR_init();                 // init the ISR's
         IRQ_init();                 // init the IRQ's and pic
         timer_install();            // register timer in irq
    +#if 1
         keybrd_install();           // register keyboard in irq
    -
    -    init_serial();              // init the COM1 port (for qemu and bochs)
    +#endif
    
         write_serial('\n');         // print a new line to seperate the qemu things
    
    diff --git a/src/kernel/irq.c b/src/kernel/irq.c
    index d3ac3fa..6140a4d 100644
    --- a/src/kernel/irq.c
    +++ b/src/kernel/irq.c
    @@ -10,7 +10,9 @@ void IRQ_handler(Registers_t *regs){
         u8 irq = regs->interrupt - PIC_OFFSET;
    
         if (g_IRQHandlers[irq] != 0){
    +        //dbgprt("IRQ_handler: ENTER irq=%u\n",irq);
             g_IRQHandlers[irq](regs);
    +        //dbgprt("IRQ_handler: EXIT\n");
         } else {
             QemuPrintf("Unhandled IRQ %d\n", irq);
         }
    diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c
    index 7136655..4dbf35a 100644
    --- a/src/kernel/kernel.c
    +++ b/src/kernel/kernel.c
    @@ -9,19 +9,62 @@
     void main(){
         HAL_init();
         set_palette();
    +    dbgprt("SCREEN_SIZE=0x%x\n",SCREEN_SIZE);
    +    dbgprt("STACK=0x%x\n",__builtin_frame_address(0));
    
         screen_swap();
    +#if 0
         clear_screen(COLOR(0,0,0));
    +#else
    +    clear_screen(COLOR(0,255,0));
    +#endif
         screen_swap();
    +    clear_screen(COLOR(0,255,0));
    +    screen_swap();
    +
    +   u64 otimer = get_timer();
    +   dbgprt("main: OTIMER otimer=%llu\n",otimer);
    +
    +   u32 kcount = 0;
    +   u8 color = 0;
    +   bool kpress_old = 0;
    +   bool kpress_cur = 0;
    +
    +    while (true) {
    +       //dbgprt("main: ENTER/GTIMER\n");
    +       u64 ctimer = get_timer();
    +       //dbgprt("main: EXIT/GTIMER\n");
    
    -    while (true){
    -        if (is_key_pressed('a')){
    -            clear_screen(COLOR(255,0,0));
    +       if (ctimer != otimer) {
    +           //dbgprt("\rmain: CTIMER %llu\n",ctimer);
    +           otimer = ctimer;
    +       }
    +
    +        kpress_cur = is_key_pressed('a');
    +
    +       if ((kpress_cur != kpress_old) && kpress_cur) {
    +           dbgprt("\n");
    +           dbgprt("main: KTIMER kcount=%u ctimer=%llu\n",ctimer);
    +           switch (kcount % 3) {
    +           case 0:
    +               color = COLOR(255,0,0);
    +               break;
    +           case 1:
    +               color = COLOR(0,255,0);
    +               break;
    +           default:
    +               color = COLOR(0,0,255);
    +               break;
    +           }
    +           ++kcount;
    +            clear_screen(color);
                 screen_swap();
             }
    +
    +       kpress_old = kpress_cur;
         }
    
    -    while(1){
    +    while (1) {
             __asm__ volatile("hlt");
         }
     }
    diff --git a/src/kernel/kernel_entry.asm b/src/kernel/kernel_entry.asm
    index d9a22bf..8e3e9c7 100644
    --- a/src/kernel/kernel_entry.asm
    +++ b/src/kernel/kernel_entry.asm
    @@ -1,8 +1,31 @@
     [bits    32]
     [extern main]
    
    +KSTACK_SIZE        equ     64 * 1024       ; stack size 64 KB
    +
    +extern __end
    +
     section .entry
     global _start
     _start:
    +   ;;;mov      esp,__end               ; this didn't work too well
    +   mov     esp,__stack                 ; point to start of stack
    +
    +   add     esp,KSTACK_SIZE             ; point past the end
    +
    +   ; get mask to align to 16 byte boundary
    +   mov     eax,0x0f
    +   not     eax
    +
    +   ; align stack
    +   and     esp,eax
    +
    +   ; point stack pointer to last valid part of stack
    +   sub     esp,16
    +
         call main
         hlt
    +
    +section .bss
    +__stack:
    +   times KSTACK_SIZE   db 0
    diff --git a/src/kernel/keyboard.h b/src/kernel/keyboard.h
    index 8091212..8476a61 100644
    --- a/src/kernel/keyboard.h
    +++ b/src/kernel/keyboard.h
    @@ -88,6 +88,7 @@ enum KEYBOARD_SPECIAL_KEYS {
         DOWN               = 0x80
     };
    
    +void keybrd_install();
     bool is_special_key(u8 key);                // check in mode if key is pressed
     bool is_key_pressed(u8 key);                // check if key is pressed
    
    diff --git a/src/kernel/printf.c b/src/kernel/printf.c
    index 435d799..af5f85e 100644
    --- a/src/kernel/printf.c
    +++ b/src/kernel/printf.c
    @@ -1,4 +1,5 @@
     #include "util.h"
    +#include <stdarg.h>
    
     // define qemu logs
     void QemuPrintStr(i8* s){
    @@ -9,12 +10,12 @@ void QemuPrintStr(i8* s){
    
     // convert binary number to ascii
     const i8 g_chars[] = "0123456789abcdef";
    -void printf_unsigned(u32 num, u32 base){
    +void printf_unsigned(u64 num, u32 base){
         i8 string[32];
         i32 pos = 0;
    
         do {
    -        u32 rem = num % base;
    +        u64 rem = num % base;
             num /= base;
             string[pos++] = g_chars[rem];
         } while (num > 0);
    @@ -24,7 +25,7 @@ void printf_unsigned(u32 num, u32 base){
         }
     }
    
    -void printf_signed(i32 num, u32 base){
    +void printf_signed(s64 num, u32 base){
         if (num < 0){
             write_serial('-'); // changed
             printf_unsigned(-num, base);
    @@ -36,11 +37,22 @@ void printf_signed(i32 num, u32 base){
     #define STATE_NORMAL 0
     #define STATE_FORMAT 1
     void QemuPrintf(const i8* str, ...){
    +#if 0
         u32* argp = (u32*)&str;
    +#endif
         u8 state = STATE_NORMAL;
    +   int llflg;
    
    +#if 0
         argp += sizeof(str)/sizeof(u32);
    +#else
    +   va_list ap;
    +   va_start(ap,str);
    +   u64 uval;
    +   s64 sval;
    +#endif
    +
         while (*str){
             switch (state){
                 case STATE_NORMAL:                      // wait for % or print
    @@ -55,35 +67,59 @@ void QemuPrintf(const i8* str, ...){
                     }
                 // fall through
                 case STATE_FORMAT:                      // check what char is after %
    +               llflg = 0;
    +               if (*str == 'l') {
    +                   ++llflg;
    +                   ++str;
    +               }
    +               if (*str == 'l') {
    +                   ++llflg;
    +                   ++str;
    +               }
    +
                     switch (*str){
                         case '%':
                             write_serial('%'); // changed
                             break;
    
                         case 'c':                       // print char
    -                        write_serial((char)*argp); // changed
    -                        argp++;
    +                        write_serial(va_arg(ap,int)); // changed
                             break;
    
                         case 's':                       // print string
    -                        QemuPrintStr((char*)*argp);
    -                        argp++;
    +                        QemuPrintStr(va_arg(ap,char *));
                             break;
    
                         case 'd':                       // print signed base 10
    -                        printf_signed((i32)*argp, 10);
    -                        argp++;
    +                       switch (llflg) {
    +                       case 0:
    +                           sval = va_arg(ap,i32);
    +                           break;
    +                       case 1:
    +                           sval = va_arg(ap,long);
    +                           break;
    +                       default:
    +                           sval = va_arg(ap,long long);
    +                           break;
    +                       }
    +                        printf_signed(sval, 10);
                             break;
    
                         case 'u':                       // print unsigned base 10
    -                        printf_unsigned((u32)*argp, 10);
    -                        argp++;
    -                        break;
    -
                         case 'x':
                         case 'p':                       // print unsigned base 16
    -                        printf_unsigned((u32)*argp, 16);
    -                        argp++;
    +                       switch (llflg) {
    +                       case 0:
    +                           uval = va_arg(ap,u32);
    +                           break;
    +                       case 1:
    +                           uval = va_arg(ap,unsigned long);
    +                           break;
    +                       default:
    +                           uval = va_arg(ap,unsigned long long);
    +                           break;
    +                       }
    +                        printf_unsigned(uval, (*str == 'u') ? 10 : 16);
                             break;
    
                         default: break;                 // ignore invalid char
    @@ -93,5 +129,10 @@ void QemuPrintf(const i8* str, ...){
             }
             str++;
         }
    +
    +#if 1
    +   va_end(ap);
    +#endif
    +
         return;
     }
    diff --git a/src/kernel/screen.c b/src/kernel/screen.c
    index 45f1de1..3713ad3 100644
    --- a/src/kernel/screen.c
    +++ b/src/kernel/screen.c
    @@ -7,21 +7,39 @@ static u8 *BUFFER = (u8*) 0xA0000;
     u8 buffers[2][SCREEN_SIZE];
     u8 current_buffer = 0;
    
    +#if 0
     #define CURRENT     (buffers[current_buffer])
    +#else
    +#define CURRENT     (&buffers[current_buffer][0])
    +#endif
    +#if 0
     #define SWAP()      (current_buffer = 1 - current_buffer)
    +#else
    +#define SWAP()      current_buffer = current_buffer ? 0 : 1
    +#endif
    
     #define VGA_MASK        0x3c6
     #define VGA_READ_ADD    0x3c7
     #define VGA_WRITE_ADD   0x3c8
     #define VGA_DATA        0x3c9
    
    +#define CURRENT_SHOW \
    +   dbgprt("%s: BUFFER=0x%x current_buffer=%d/0x%x/0x%x\n", \
    +       __FUNCTION__,BUFFER,current_buffer,CURRENT,CURRENT + SCREEN_SIZE)
    +
     void screen_swap(){
    +   dbgprt("screen_swap: ENTER\n");
    +   //CURRENT_SHOW;
         memcpy(BUFFER, CURRENT, SCREEN_SIZE);
         SWAP();
    +   dbgprt("screen_swap: EXIT\n");
     }
    
     void clear_screen(u8 color){
    -    memset(&CURRENT, color, SCREEN_SIZE);
    +   dbgprt("clear_screen: ENTER color=0x%x\n",color);
    +   CURRENT_SHOW;
    +    memset(CURRENT, color, SCREEN_SIZE);
    +   dbgprt("clear_screen: EXIT\n");
     }
    
     void plot_pixel(i32 x, i32 y, u8 color){
    @@ -30,6 +48,7 @@ void plot_pixel(i32 x, i32 y, u8 color){
     }
    
     void set_palette(){
    +   dbgprt("set_pallette: ENTER\n");
         outb(VGA_WRITE_ADD, 0);
         for(u8 i=0; i < 255; i++){
             // we shift it by 2 because each color is 6 bits
    @@ -43,6 +62,7 @@ void set_palette(){
         outb(VGA_DATA, 0xff >> 2);
         outb(VGA_DATA, 0xff >> 2);
         outb(VGA_DATA, 0xff >> 2);
    +   dbgprt("set_pallette: EXIT\n");
     }
    
     u8 COLOR(u8 r, u8 g, u8 b){
    diff --git a/src/kernel/timer.c b/src/kernel/timer.c
    index 748de08..7e283c0 100644
    --- a/src/kernel/timer.c
    +++ b/src/kernel/timer.c
    @@ -2,36 +2,38 @@
     #include "util.h"
     #include "irq.h"
    
    -static struct {
    -    u32 divisor;
    -    u32 frequency;
    -    u64 ticks;
    -} state;
    -
     /* u64 timer = 0; */
     void timerFunction(Registers_t *regs){
    -    state.ticks++;
    +    timer_state.ticks++;
    +   //dbgprt("timerFunction: ticks=%llu\n",timer_state.ticks);
     }
    
     void PIT_setCount(){
         // set the frequency of the pit to 1/TIMER_FREQ
    -    state.divisor = TIMER_FREQ;
    -    state.frequency = PIT_INTERNAL_FREQUENCY / state.divisor;      // get the desired freq
    +    timer_state.divisor = TIMER_FREQ;
    +    timer_state.frequency = PIT_INTERNAL_FREQUENCY / timer_state.divisor;      // get the desired freq
    
         CLI();
         outb(PIT_COMMAND, PIT_SET);
    -    outb(PIT_CHANNEL0, state.frequency & 0xff);                  // low byte
    -    outb(PIT_CHANNEL0, ((state.frequency & 0xff00) >> 8));       // high byte
    +    outb(PIT_CHANNEL0, timer_state.frequency & 0xff);                  // low byte
    +    outb(PIT_CHANNEL0, ((timer_state.frequency & 0xff00) >> 8));       // high byte
         STI();
    
         return;
     }
    
     u64 get_timer(){
    -    return state.ticks;
    +    return timer_state.ticks;
     }
    
     void timer_install(){
    +   dbgprt("timer_install: ENTER\n");
    +#if 0
         PIT_setCount();
         IRQ_RegisterHandler(0, timerFunction);
    +#else
    +    IRQ_RegisterHandler(0, timerFunction);
    +    PIT_setCount();
    +#endif
    +   dbgprt("timer_install: EXIT\n");
     }
    diff --git a/src/kernel/timer.h b/src/kernel/timer.h
    index 9d6de1e..daecf36 100644
    --- a/src/kernel/timer.h
    +++ b/src/kernel/timer.h
    @@ -13,6 +13,14 @@
    
     #define PIT_SET         0x36
    
    +struct timer {
    +    u32 divisor;
    +    u32 frequency;
    +    u64 ticks;
    +};
    +
    +struct timer timer_state;
    +
     void timer_install();
     u64 get_timer();
    
    diff --git a/src/kernel/util.h b/src/kernel/util.h
    index df2e92b..4935576 100644
    --- a/src/kernel/util.h
    +++ b/src/kernel/util.h
    @@ -10,6 +10,8 @@ typedef char i8;
     typedef short i16;
     typedef int i32;
     typedef long long i64;
    +typedef int s32;
    +typedef long long s64;
     typedef u32 size_t;
     typedef u32 uintptr_t;
     typedef float f32;
    @@ -41,6 +43,9 @@ typedef u8 bool;
     void QemuPrintStr(i8* s);
     void QemuPrintf(const i8* str, ...);
    
    +#define dbgprt(_fmt...) \
    +   QemuPrintf(_fmt)
    +
     // communiation via ports --> io.c
     int init_serial();
     void write_serial(char a);
    

    UPDATE #2:

    Thank you so much, now it works!! There is something I didn't quite understand... why use two buffers, why buffers is two arrays in the size of screen. Why can't we just write to the same array each time and then copy it to the screen memory? – neta cohen

    Yes, you are quite correct. You don't need two buffers. But, it's your code, so why did you do this? ;-)

    Just one is needed because filling buffers and then "blitting" to the frame buffer (i.e. the memcpy) is [already] "double buffering"

    It would actually be faster with just one buffers. After sending the first frame to the frame buffer, generating the second frame could be just a small incremental change to buffers

    This is similar to using a graphics library like SDL2.

    1. You create a surface (equiv to your buffers), draw into that surface. Or, just draw into the offscreen buffer with (e.g.) SDL_RenderDrawRect
    2. Then, blit to the screen with SDL_RenderPresent (like your memcpy).

    I've seen this in some other similar SO questions for this like the linked answer. The github repo linked to on that page is [AFAICT] no longer available. But, it had a buffer that was just text chars, it would change those, then use those to index into a font to produce the raster lines in the frame buffer.

    Really, I don't know why, so the following is just speculation ...

    Although not really true on modern systems, IIRC [and I could easily be wrong about this] access to the frame buffer could be slower than regular RAM on older systems. So, with two offscreen buffers, we could do memcpy only for the pixels that change frame-to-frame.

    Or, with multiple buffers, it could help with animation (loosely):

    1. Read data from .gif file into buffers[0]
    2. Start I/O for reading of next frame into buffers[1]
    3. Do memcpy of buffers[0] to H/W frame buffer

    So, reading data from a file for frame N could be overlapped with the memcpy for frame N-1. This could be more efficient.