diff --git a/asm/x86-unknown-none/bootloader.asm b/asm/x86-unknown-none/bootloader.asm
index 95ec773de5e8f1be81ea83b5d70d2f2f08bea3c3..d616e5584b860c981ac277961e88911bacf0b36e 100644
--- a/asm/x86-unknown-none/bootloader.asm
+++ b/asm/x86-unknown-none/bootloader.asm
@@ -7,8 +7,15 @@ stage2:
     align 512, db 0
 stage2.end:
 
+; the maximum size of stage1 + stage2 is 5 KiB
+; this places stage3 at 0x9000
+times 5120-($-$$) db 0
+
 stage3:
     %defstr STAGE3_STR %[STAGE3]
     incbin STAGE3_STR
     align 512, db 0
 .end:
+
+; the maximum size of the boot loader portion is 128 KiB
+times 131072-($-$$) db 0
diff --git a/asm/x86-unknown-none/cpuid.asm b/asm/x86-unknown-none/cpuid.asm
index a1c4f755dce6ebb1df29f15c983407d876ad218c..715733d9d085c054ca80569aae81796284b7f368 100644
--- a/asm/x86-unknown-none/cpuid.asm
+++ b/asm/x86-unknown-none/cpuid.asm
@@ -3,7 +3,7 @@ USE16
 
 cpuid_required_features:
     .edx equ cpuid_edx.fpu | cpuid_edx.sse | cpuid_edx.pae | cpuid_edx.pse | cpuid_edx.pge | cpuid_edx.fxsr
-    .ecx equ cpuid_ecx.xsave
+    .ecx equ 0
 
 cpuid_check:
     mov eax, 1
@@ -20,8 +20,8 @@ cpuid_check:
     ret
 
 .error:
-    push edx
     push ecx
+    push edx
 
     mov si, .msg_features
     call print
@@ -29,7 +29,7 @@ cpuid_check:
     mov si, .msg_line
     call print
 
-    mov si, .msg_ecx
+    mov si, .msg_edx
     call print
 
     pop ebx
@@ -43,22 +43,22 @@ cpuid_check:
     mov si, .msg_must_contain
     call print
 
-    mov ebx, cpuid_required_features.ecx
+    mov ebx, cpuid_required_features.edx
     shr ebx, 16
     call print_hex
 
-    mov ebx, cpuid_required_features.ecx
+    mov ebx, cpuid_required_features.edx
     call print_hex
 
     mov si, .msg_line
     call print
 
-    mov si, .msg_edx
+    mov si, .msg_ecx
     call print
 
     pop ebx
     push ebx
-    rol ebx, 16
+    shr ebx, 16
     call print_hex
 
     pop ebx
@@ -67,11 +67,11 @@ cpuid_check:
     mov si, .msg_must_contain
     call print
 
-    mov ebx, cpuid_required_features.edx
+    mov ebx, cpuid_required_features.ecx
     shr ebx, 16
     call print_hex
 
-    mov ebx, cpuid_required_features.edx
+    mov ebx, cpuid_required_features.ecx
     call print_hex
 
     mov si, .msg_line
@@ -84,8 +84,8 @@ cpuid_check:
 
 .msg_features: db "Required CPU features are not present",0
 .msg_line: db 13,10,0
-.msg_ecx: db "ECX ",0
 .msg_edx: db "EDX ",0
+.msg_ecx: db "ECX ",0
 .msg_must_contain: db " must contain ",0
 
 cpuid_edx:
diff --git a/asm/x86-unknown-none/memory_map.asm b/asm/x86-unknown-none/memory_map.asm
deleted file mode 100644
index 460ea27b746200d233eb8418f842aece1c0b2d50..0000000000000000000000000000000000000000
--- a/asm/x86-unknown-none/memory_map.asm
+++ /dev/null
@@ -1,33 +0,0 @@
-SECTION .text
-USE16
-
-;Generate a memory map at 0x500 to 0x5000 (available memory not used for kernel or bootloader)
-memory_map:
-.start  equ 0x0500
-.end    equ 0x5000
-.length equ .end - .start
-
-    xor eax, eax
-    mov di, .start
-    mov ecx, .length / 4 ; moving 4 Bytes at once
-    cld
-    rep stosd
-
-    mov di, .start
-    mov edx, 0x534D4150
-    xor ebx, ebx
-.lp:
-    mov eax, 0xE820
-    mov ecx, 24
-
-    int 0x15
-    jc .done ; Error or finished
-
-    cmp ebx, 0
-    je .done ; Finished
-
-    add di, 24
-    cmp di, .end
-    jb .lp ; Still have buffer space
-.done:
-    ret
diff --git a/asm/x86-unknown-none/stage1.asm b/asm/x86-unknown-none/stage1.asm
index 815d1af4274c4b8c5e7ea4495719b99efcd34414..26740c958e39e017e9e79581f506e28fe376dc31 100644
--- a/asm/x86-unknown-none/stage1.asm
+++ b/asm/x86-unknown-none/stage1.asm
@@ -28,11 +28,10 @@ stage1: ; dl comes with disk
 
     mov eax, (stage2 - stage1) / 512
     mov bx, stage2
-    mov cx, (stage2.end - stage2) / 512
-    xor dx, dx
+    mov cx, (stage3.end - stage2) / 512
+    mov dx, 0
     call load
 
-    call print_line
     mov si, finished
     call print
     call print_line
@@ -79,9 +78,6 @@ load:
     ret
 
 print_dapack:
-    mov al, 13
-    call print_char
-
     mov bx, [DAPACK.addr + 2]
     call print_hex
 
@@ -106,6 +102,8 @@ print_dapack:
     mov bx, [DAPACK.buf]
     call print_hex
 
+    call print_line
+
     ret
 
 error:
diff --git a/asm/x86-unknown-none/stage2.asm b/asm/x86-unknown-none/stage2.asm
index baa3f6b0ee0586a25c81f408630b62ba25e508eb..7455820188f5ae7459eb39800ad300eb08bafcb6 100644
--- a/asm/x86-unknown-none/stage2.asm
+++ b/asm/x86-unknown-none/stage2.asm
@@ -10,112 +10,22 @@ stage2.entry:
     or al, 2
     out 0x92, al
 
-    ; load memory map
-    ;TODO: rewrite this in Rust
-    call memory_map
-
-    mov edi, [args.stage3_base]
-    mov ecx, (stage3.end - stage3)
-    mov [args.stage3_size], ecx
-
-    mov eax, (stage3 - stage1)/512
-    add ecx, 511
-    shr ecx, 9
-    call load_extent
-
     mov dword [protected_mode.func], stage3.entry
     jmp protected_mode.entry
 
-args:
-    .stage3_base dq 0x100000
-    .stage3_size dq 0
-
-; load a disk extent into high memory
-; eax - sector address
-; ecx - sector count
-; edi - destination
-load_extent:
-    ; loading stage3 to 1MiB
-    ; move part of stage3 to stage2.end via bootsector#load and then copy it up
-    ; repeat until all of the stage3 is loaded
-    buffer_size_sectors equ 127
-
-.lp:
-    cmp ecx, buffer_size_sectors
-    jb .break
-
-    ; saving counter
-    push eax
-    push ecx
-
-    push edi
-
-    ; populating buffer
-    mov ecx, buffer_size_sectors
-    mov bx, stage2.end
-    mov dx, 0x0
-
-    ; load sectors
-    call load
-
-    ; set up unreal mode
-    call unreal
-
-    pop edi
-
-    ; move data
-    mov esi, stage2.end
-    mov ecx, buffer_size_sectors * 512 / 4
-    cld
-    a32 rep movsd
-
-    pop ecx
-    pop eax
-
-    add eax, buffer_size_sectors
-    sub ecx, buffer_size_sectors
-    jmp .lp
-
-.break:
-    ; load the part of the stage3 that does not fill the buffer completely
-    test ecx, ecx
-    jz .finish ; if cx = 0 => skip
-
-    push ecx
-    push edi
-
-    mov bx, stage2.end
-    mov dx, 0x0
-    call load
-
-    ; moving remnants of stage3
-    call unreal
-
-    pop edi
-    pop ecx
-
-    mov esi, stage2.end
-    shl ecx, 7 ; * 512 / 4
-    cld
-    a32 rep movsd
-
-.finish:
-    call print_line
-    ret
-
 %include "cpuid.asm"
 %include "gdt.asm"
 %include "long_mode.asm"
-%include "memory_map.asm"
 %include "protected_mode.asm"
 %include "thunk.asm"
-%include "unreal.asm"
 
 USE32
+
 stage3.entry:
-    mov esp, 0x800000 - 128
+    ; stage3 stack at 256 KiB
+    mov esp, 0x40000
 
-    ; entry point
+    ; push arguments
     mov eax, thunk.int16
     push eax
     mov eax, thunk.int15
@@ -127,8 +37,8 @@ stage3.entry:
     xor eax, eax
     mov al, [disk]
     push eax
-    mov eax, [args.stage3_base]
-    call [eax + 0x18]
+    mov eax, [stage3 + 0x18]
+    call eax
 .halt:
     cli
     hlt
diff --git a/asm/x86-unknown-none/unreal.asm b/asm/x86-unknown-none/unreal.asm
deleted file mode 100644
index b26397102eea203602788181ebb080831396e8e6..0000000000000000000000000000000000000000
--- a/asm/x86-unknown-none/unreal.asm
+++ /dev/null
@@ -1,35 +0,0 @@
-SECTION .text
-USE16
-
-; switch to unreal mode; ds and es can address up to 4GiB
-unreal:
-    cli
-
-    lgdt [gdtr]
-
-    push es
-    push ds
-
-    mov  eax, cr0          ; switch to pmode by
-    or al,1                ; set pmode bit
-    mov  cr0, eax
-
-    jmp $+2
-
-; http://wiki.osdev.org/Babystep7
-; When this register given a "selector", a "segment descriptor cache register"
-; is filled with the descriptor values, including the size (or limit). After
-; the switch back to real mode, these values are not modified, regardless of
-; what value is in the 16-bit segment register. So the 64k limit is no longer
-; valid and 32-bit offsets can be used with the real-mode addressing rules
-    mov bx, gdt.pm32_data
-    mov es, bx
-    mov ds, bx
-
-    and al,0xFE            ; back to realmode
-    mov  cr0, eax          ; by toggling bit again
-
-    pop ds
-    pop es
-    sti
-    ret
diff --git a/linkers/x86-unknown-none.ld b/linkers/x86-unknown-none.ld
index abaf92bd00f32cff73fc111d2a596cdb1ec43193..7eb1b86dc32aaa159824bc12573190313445e037 100644
--- a/linkers/x86-unknown-none.ld
+++ b/linkers/x86-unknown-none.ld
@@ -2,7 +2,8 @@ ENTRY(kstart)
 OUTPUT_FORMAT(elf32-i386)
 
 SECTIONS {
-    . = 0x100000;
+    /* The start address must match bootloader.asm */
+    . = 0x9000;
 
     . += SIZEOF_HEADERS;
     . = ALIGN(4096);
diff --git a/src/lib.rs b/src/lib.rs
index ad715ac46dac40f6a38d5cf8fa5487e41482b06c..dcb365d7b8dcfa41a1ce957e75222be438a04cda 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,22 +8,28 @@ extern crate alloc;
 
 use alloc::vec::Vec;
 use core::{
+    alloc::{GlobalAlloc, Layout},
     cmp,
     fmt::{self, Write},
     ptr,
     slice,
 };
 use linked_list_allocator::LockedHeap;
-use log::{error, info};
+use spin::Mutex;
 
 use self::disk::DiskBios;
 use self::logger::LOGGER;
+use self::memory_map::memory_map;
 use self::thunk::ThunkData;
 use self::vbe::{VbeCardInfo, VbeModeInfo};
 use self::vga::{VgaTextBlock, VgaTextColor, Vga};
 
+#[macro_use]
+mod macros;
+
 mod disk;
 mod logger;
+mod memory_map;
 mod paging;
 mod panic;
 mod thunk;
@@ -34,6 +40,7 @@ mod vga;
 // 0x500 to 0x7BFF is free
 const VBE_CARD_INFO_ADDR: usize = 0x500; // 512 bytes, ends at 0x6FF
 const VBE_MODE_INFO_ADDR: usize = 0x700; // 256 bytes, ends at 0x7FF
+const MEMORY_MAP_ADDR: usize = 0x800; // 24 bytes, ends at 0x817
 const DISK_ADDRESS_PACKET_ADDR: usize = 0x0FF0; // 16 bytes, ends at 0x0FFF
 const DISK_BIOS_ADDR: usize = 0x1000; // 4096 bytes, ends at 0x1FFF
 const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards
@@ -42,7 +49,9 @@ const VGA_ADDR: usize = 0xB8000;
 #[global_allocator]
 static ALLOCATOR: LockedHeap = LockedHeap::empty();
 
-static mut VGA: Vga = unsafe { Vga::new(VGA_ADDR as *mut VgaTextBlock, 80, 25) };
+static VGA: Mutex<Vga> = Mutex::new(
+    unsafe { Vga::new(VGA_ADDR, 80, 25) }
+);
 
 static mut KERNEL_PHYS: u64 = 0;
 
@@ -71,59 +80,71 @@ pub unsafe extern "C" fn kstart(
 
     {
         // Clear VGA console
-        let blocks = VGA.blocks();
+        let mut vga = VGA.lock();
+        let blocks = vga.blocks();
         for i in 0..blocks.len() {
             blocks[i] = VgaTextBlock {
                 char: 0,
-                color: ((VGA.bg as u8) << 4) | (VGA.fg as u8),
+                color: ((vga.bg as u8) << 4) | (vga.fg as u8),
             };
         }
     }
 
-    // Initialize allocator at the end of stage 3 with a meager 1 MiB, which we can be pretty
-    // sure will be available.
-    extern "C" {
-        static mut __end: u8;
-    }
-    let heap_start = &__end as *const _ as usize;
-    let heap_size = 1024 * 1024;
-    ALLOCATOR.lock().init(heap_start, heap_size);
-
     // Set logger
     LOGGER.init();
 
-    // Locate RedoxFS
-    {
+    let (heap_start, heap_size) = memory_map(thunk15).expect("no memory for heap");
+
+    println!("HEAP: {:X}:{:X}", heap_start, heap_size);
+    ALLOCATOR.lock().init(heap_start, heap_size);
+
+    // Locate kernel on RedoxFS
+    let kernel = {
         //TODO: ensure boot_disk is 8-bit
-        info!("DISK {:02X}", boot_disk);
+        println!("BIOS Disk: {:02X}", boot_disk);
         let disk = DiskBios::new(boot_disk as u8, thunk13);
+
         //TODO: get block from partition table
         let block = 1024 * 1024 / redoxfs::BLOCK_SIZE;
-        match redoxfs::FileSystem::open(disk, Some(block)) {
-            Ok(mut fs) => {
-                info!("RedoxFS {} MiB", fs.header.1.size / 1024 / 1024);
-
-                match fs.find_node("kernel", fs.header.1.root) {
-                    Ok(node) => match fs.node_len(node.0) {
-                        Ok(len) => {
-                            info!("Kernel {} MiB", len / 1024 / 1024);
-                        },
-                        Err(err) => {
-                            error!("Failed to read kernel file length: {:?}", err);
-                        }
-                    },
-                    Err(err) => {
-                        error!("Failed to find kernel file: {:?}", err);
-                    }
-                }
-            },
-            Err(err) => {
-                error!("Failed to open RedoxFS: {:?}", err);
-            }
+        let mut fs = redoxfs::FileSystem::open(disk, Some(block))
+            .expect("Failed to open RedoxFS");
+
+        println!("RedoxFS Size: {} MiB", fs.header.1.size / 1024 / 1024);
+
+        let node = fs.find_node("kernel", fs.header.1.root)
+            .expect("failed to find kernel file");
+
+        let size = fs.node_len(node.0)
+            .expect("failed to read kernel size");
+
+        println!("Kernel Size: {} MiB", size / 1024 / 1024);
+
+        let ptr = ALLOCATOR.alloc_zeroed(
+            Layout::from_size_align(size as usize, 4096).unwrap()
+        );
+        if ptr.is_null() {
+            panic!("Failed to allocate memory for kernel");
         }
-    }
 
-    //let page_phys = paging::paging_create(KERNEL_PHYS);
+        let kernel = slice::from_raw_parts_mut(
+            ptr,
+            size as usize
+        );
+
+        let mut i = 0;
+        for chunk in kernel.chunks_mut(1024 * 1024) {
+            print!("\rKernel Loading: {}%", i * 100 / size);
+            i += fs.read_node(node.0, i, chunk, 0, 0)
+                .expect("Failed to read kernel file") as u64;
+        }
+        println!("\rKernel Loading: 100%");
+
+        kernel
+    };
+
+    println!("Kernel Phys: 0x{:X}", kernel.as_ptr() as u64);
+    let page_phys = paging::paging_create(kernel.as_ptr() as u64);
+    panic!("kernel entry not implemented");
 
     let mut modes = Vec::new();
     {
@@ -182,22 +203,22 @@ pub unsafe extern "C" fn kstart(
                         format!("{:>4}x{:<4} {:>3}:{:<3}", w, h, aspect_w, aspect_h)
                     ));
                 } else {
-                    error!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, data.eax);
+                    panic!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, data.eax);
                 }
             }
         } else {
-            error!("Failed to read VBE card info: 0x{:04X}", data.eax);
+            panic!("Failed to read VBE card info: 0x{:04X}", data.eax);
         }
     }
 
     // Sort modes by pixel area, reversed
     modes.sort_by(|a, b| (b.1 * b.2).cmp(&(a.1 * a.2)));
 
-    writeln!(VGA, "Arrow keys and enter select mode").unwrap();
+    println!("Arrow keys and enter select mode");
 
     //TODO 0x4F03 VBE function to get current mode
-    let off_x = VGA.x;
-    let off_y = VGA.y;
+    let off_x = VGA.lock().x;
+    let off_y = VGA.lock().y;
     let rows = 12;
     let mut selected = modes.get(0).map_or(0, |x| x.0);
     loop {
@@ -209,18 +230,18 @@ pub unsafe extern "C" fn kstart(
                 row = 0;
             }
 
-            VGA.x = off_x + col * 20;
-            VGA.y = off_y + row;
+            VGA.lock().x = off_x + col * 20;
+            VGA.lock().y = off_y + row;
 
             if *mode == selected {
-                VGA.bg = VgaTextColor::White;
-                VGA.fg = VgaTextColor::Black;
+                VGA.lock().bg = VgaTextColor::White;
+                VGA.lock().fg = VgaTextColor::Black;
             } else {
-                VGA.bg = VgaTextColor::DarkGray;
-                VGA.fg = VgaTextColor::White;
+                VGA.lock().bg = VgaTextColor::DarkGray;
+                VGA.lock().fg = VgaTextColor::White;
             }
 
-            write!(VGA, "{}", text).unwrap();
+            print!("{}", text);
 
             row += 1;
         }
diff --git a/src/logger.rs b/src/logger.rs
index d6c225f6c81dcedfcac1586e1b5fe593ce835e58..7c498a9a91b68159772e0d4f6ed4e0bd503e5546 100644
--- a/src/logger.rs
+++ b/src/logger.rs
@@ -1,8 +1,6 @@
 use core::fmt::Write;
 use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError};
 
-use crate::VGA;
-
 pub static LOGGER: Logger = Logger;
 
 pub struct Logger;
@@ -21,9 +19,7 @@ impl Log for Logger {
 
     fn log(&self, record: &Record) {
         if self.enabled(record.metadata()) {
-            unsafe {
-                writeln!(VGA, "{} - {}", record.level(), record.args()).unwrap();
-            }
+            println!("{} - {}", record.level(), record.args());
         }
     }
 
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0775c1c536f7691916bc33c4f2b799c2758468e7
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,16 @@
+/// Print to console
+#[macro_export]
+macro_rules! print {
+    ($($arg:tt)*) => ({
+        use core::fmt::Write;
+        write!($crate::VGA.lock(), $($arg)*).unwrap();
+    });
+}
+
+/// Print with new line to console
+#[macro_export]
+macro_rules! println {
+    () => (print!("\n"));
+    ($fmt:expr) => (print!(concat!($fmt, "\n")));
+    ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
+}
diff --git a/src/memory_map.rs b/src/memory_map.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8aa986f55f2005f3cbe51dfff341a8d257ab9cac
--- /dev/null
+++ b/src/memory_map.rs
@@ -0,0 +1,54 @@
+use core::{cmp, mem, ptr};
+
+use crate::thunk::ThunkData;
+
+#[repr(packed)]
+struct MemoryMapEntry {
+    pub base: u64,
+    pub length: u64,
+    pub kind: u32,
+}
+
+pub unsafe fn memory_map(thunk15: extern "C" fn()) -> Option<(usize, usize)> {
+    let mut heap_limits = None;
+    let mut data = ThunkData::new();
+    loop {
+        let index = data.ebx;
+
+        data.eax = 0xE820;
+        data.ecx = mem::size_of::<MemoryMapEntry>() as u32;
+        data.edx = 0x534D4150;
+        data.edi = crate::MEMORY_MAP_ADDR as u32;
+
+        data.with(thunk15);
+
+        assert_eq!(data.eax, 0x534D4150);
+        assert_eq!(data.ecx, mem::size_of::<MemoryMapEntry>() as u32);
+        let entry = ptr::read(crate::MEMORY_MAP_ADDR as *const MemoryMapEntry);
+
+        println!("MEM {}: 0x{:016X} 0x{:016X} 0x{:08X}", index, entry.base, entry.length, entry.kind);
+
+        //TODO: There is a problem with QEMU crashing if we write at about 8 MiB, so skip to 16
+        let heap_start = 16 * 1024 * 1024;
+        if (
+            entry.kind == 1 &&
+            entry.base <= heap_start as u64 &&
+            (entry.base + entry.length) >= heap_start as u64
+        ) {
+            let heap_end = cmp::min(
+                entry.base + entry.length,
+                usize::MAX as u64
+            ) as usize;
+            if heap_end >= heap_start {
+                heap_limits = Some((
+                    heap_start,
+                    heap_end - heap_start
+                ));
+            }
+        }
+
+        if data.ebx == 0 {
+            return heap_limits;
+        }
+    }
+}
diff --git a/src/panic.rs b/src/panic.rs
index 9d817bfe1545ea3c10598caa46aecdb3472efb25..d2cf0dfbc357f90daae75ea59ed7a95143ef2ff8 100644
--- a/src/panic.rs
+++ b/src/panic.rs
@@ -1,6 +1,7 @@
 //! Intrinsics for panic handling
 
 use core::alloc::Layout;
+use core::fmt::Write;
 use core::panic::PanicInfo;
 
 #[lang = "eh_personality"]
@@ -11,9 +12,9 @@ pub extern "C" fn rust_eh_personality() {}
 #[panic_handler]
 #[no_mangle]
 pub extern "C" fn rust_begin_unwind(info: &PanicInfo) -> ! {
-    log::error!("BOOTLOADER PANIC: {}", info);
-    loop {
-        unsafe {
+    unsafe {
+        let _ = writeln!(crate::VGA.lock(), "BOOTLOADER PANIC:\n{}", info);
+        loop {
             llvm_asm!("hlt" : : : : "intel", "volatile");
         }
     }
diff --git a/src/vga.rs b/src/vga.rs
index 1987005296c07b924824dee0f1e979e101117fe1..61ae13e05714797948a6af3d6616680be38c09b3 100644
--- a/src/vga.rs
+++ b/src/vga.rs
@@ -29,7 +29,7 @@ pub enum VgaTextColor {
 }
 
 pub struct Vga {
-    pub ptr: *mut VgaTextBlock,
+    pub base: usize,
     pub width: usize,
     pub height: usize,
     pub x: usize,
@@ -39,9 +39,9 @@ pub struct Vga {
 }
 
 impl Vga {
-    pub const unsafe fn new(ptr: *mut VgaTextBlock, width: usize, height: usize) -> Self {
+    pub const unsafe fn new(base: usize, width: usize, height: usize) -> Self {
         Self {
-            ptr,
+            base,
             width,
             height,
             x: 0,
@@ -53,7 +53,7 @@ impl Vga {
 
     pub unsafe fn blocks(&mut self) -> &'static mut [VgaTextBlock] {
         slice::from_raw_parts_mut(
-            self.ptr,
+            self.base as *mut VgaTextBlock,
             self.width * self.height,
         )
     }