From faa1e924e19776d3e8b8e6c8922f068348cd9240 Mon Sep 17 00:00:00 2001
From: Eemeli <eemeli.o.lehtonen@utu.fi>
Date: Wed, 1 Feb 2023 12:01:17 +0200
Subject: [PATCH] page allocation

---
 Cargo.toml                  |    1 +
 Makefile                    |    4 +-
 cfg/limine.cfg              |    2 +-
 old_cargo_configs/runner.sh |   78 --
 src/boot/limine/addr.rs     |   18 +-
 src/boot/limine/mem.rs      |   64 +-
 src/boot/limine/mod.rs      |    2 +-
 src/main.rs                 |   37 +-
 src/mem/bump.rs             |   10 +-
 src/mem/map.rs              |   70 +-
 src/mem/mod.rs              |   90 ++-
 src/mem/pfa.rs              |  112 ---
 src/mem/pmm.rs              |  328 +++++++++
 src/video/font.rs           | 1369 ++++++++++++++++++++++++++++-------
 src/video/logger.rs         |   13 +-
 15 files changed, 1659 insertions(+), 539 deletions(-)
 delete mode 100755 old_cargo_configs/runner.sh
 delete mode 100755 src/mem/pfa.rs
 create mode 100755 src/mem/pmm.rs

diff --git a/Cargo.toml b/Cargo.toml
index 4bcd210..c6ac2e8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ spin = "0.9.4"
 volatile = "0.4.5"
 x86_64 = "0.14.10"
 uart_16550 = "0.2.18"
+# bitvec = "1.0.1"
 #tracing = { version = "0.1.37", default-features = false }
 
 [dependencies.limine]
diff --git a/Makefile b/Makefile
index 4236c51..35c767c 100644
--- a/Makefile
+++ b/Makefile
@@ -83,8 +83,8 @@ build: ${KERNEL}
 # bootable iso alias
 iso: ${HYPERION}
 
-reset-cargo-deps:
-	rm ${CARGO_DIR}/hyperion.d
+clippy:
+	${CARGO} clippy ${CARGO_FLAGS} -- -D warnings
 
 # connect gdb to qemu
 gdb:
diff --git a/cfg/limine.cfg b/cfg/limine.cfg
index 0201da3..58c8719 100644
--- a/cfg/limine.cfg
+++ b/cfg/limine.cfg
@@ -4,4 +4,4 @@ TIMEOUT=0
 :Hyperion
     PROTOCOL=limine
     KERNEL_PATH=boot:///hyperion
-    KERNEL_CMDLINE=log=trace
+    KERNEL_CMDLINE=log=debug
diff --git a/old_cargo_configs/runner.sh b/old_cargo_configs/runner.sh
deleted file mode 100755
index e66833b..0000000
--- a/old_cargo_configs/runner.sh
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env bash
-#
-# Hyperion x86_64 is runnable
-
-set -xe
-
-echo $@
-
-LIMINE_GIT_URL="https://github.com/limine-bootloader/limine.git"
-ISO_DIR=target/hyperion/x86_64/iso
-KERNEL=$1
-
-# Clone the `limine` repository if we don't have it yet.
-if [ ! -d target/limine ]; then
-    git clone $LIMINE_GIT_URL --depth=1 --branch v3.0-branch-binary target/limine
-fi
-
-# Make sure we have an up-to-date version of the bootloader.
-cd target/limine
-git fetch
-make
-cd -
-
-# Copy the needed files into an ISO image.
-mkdir -p $ISO_DIR
-cp cfg/limine.cfg target/limine/limine{.sys,-cd.bin,-cd-efi.bin} $ISO_DIR
-cp $KERNEL $ISO_DIR/hyperion
-
-xorriso -as mkisofs \
-    -b limine-cd.bin \
-    -no-emul-boot -boot-load-size 4 -boot-info-table \
-    --efi-boot limine-cd-efi.bin \
-    -efi-boot-part --efi-boot-image --protective-msdos-label \
-    $ISO_DIR -o $KERNEL.iso
-
-# For the image to be bootable on BIOS systems, we must run `limine-deploy` on it.
-target/limine/limine-deploy $KERNEL.iso
-
-# A hack to detect if the kernel is a testing kernel
-# Cargo test binary generates a 'random id' for testing binaries
-if [ "$(basename $KERNEL)" = "hyperion" ]; then
-    # Run the created image with QEMU.
-    qemu-system-x86_64 \
-        -enable-kvm \
-        -machine q35 \
-        -cpu qemu64,+rdrand,+rdseed \
-        -smp 8 \
-        -m 512m \
-        -M smm=off \
-        -d int,guest_errors,cpu_reset \
-        -no-reboot \
-        -serial stdio \
-        $KERNEL.iso
-    #-s -S \
-    #-no-shutdown \
-    #-D target/log.txt \
-else
-    set +e
-    # Run the created image with QEMU.
-    qemu-system-x86_64 \
-        -enable-kvm \
-        -machine q35 \
-        -cpu qemu64,+rdrand,+rdseed \
-        -smp 8 \
-        -m 512m \
-        -M smm=off \
-        -d int,guest_errors,cpu_reset \
-        -device isa-debug-exit,iobase=0xf4,iosize=0x04 \
-        -no-reboot \
-        -serial stdio \
-        -display none \
-        $KERNEL.iso
-    #-no-shutdown \
-    #-D target/log.txt \
-
-    [ $? -ne 33 ] && exit 1
-    exit 0
-fi
diff --git a/src/boot/limine/addr.rs b/src/boot/limine/addr.rs
index bae296d..b083eea 100644
--- a/src/boot/limine/addr.rs
+++ b/src/boot/limine/addr.rs
@@ -1,4 +1,4 @@
-use limine::{LimineKernelAddressRequest, LimineKernelAddressResponse};
+use limine::{LimineHhdmRequest, LimineKernelAddressRequest, LimineKernelAddressResponse};
 use spin::Lazy;
 use x86_64::{PhysAddr, VirtAddr};
 
@@ -12,9 +12,23 @@ pub fn virt_addr() -> VirtAddr {
     VirtAddr::new(KERNEL_ADDR.virtual_base)
 }
 
+pub fn hhdm_offset() -> u64 {
+    static HHDM_OFFSET: Lazy<u64> = Lazy::new(|| {
+        static REQ: LimineHhdmRequest = LimineHhdmRequest::new(0);
+        REQ.get_response()
+            .get()
+            .expect("Cannot get Limine HHDM response")
+            .offset
+    });
+
+    *HHDM_OFFSET
+}
+
 //
 
 static KERNEL_ADDR: Lazy<&'static LimineKernelAddressResponse> = Lazy::new(|| {
     static REQ: LimineKernelAddressRequest = LimineKernelAddressRequest::new(0);
-    REQ.get_response().get().unwrap()
+    REQ.get_response()
+        .get()
+        .expect("Cannot get Limine HHDM response")
 });
diff --git a/src/boot/limine/mem.rs b/src/boot/limine/mem.rs
index ab4b1fe..e4ebbf6 100644
--- a/src/boot/limine/mem.rs
+++ b/src/boot/limine/mem.rs
@@ -1,47 +1,37 @@
-use crate::mem::map::Memmap;
+use crate::{
+    mem::map::{Memmap, Memtype},
+    trace,
+};
+use core::sync::atomic::{AtomicBool, Ordering};
 use limine::{LimineMemmapEntry, LimineMemmapRequest, LimineMemoryMapEntryType, NonNullPtr};
-use spin::Lazy;
+use x86_64::PhysAddr;
 
 //
 
 pub fn memmap() -> impl Iterator<Item = Memmap> {
-    const DEFAULT_MEMMAP: Memmap = Memmap {
-        base: u64::MAX,
-        len: 0u64,
-    };
+    static FIRST_TIME: AtomicBool = AtomicBool::new(true);
+    let first_time = FIRST_TIME.swap(false, Ordering::SeqCst);
 
-    memiter()
-        .scan(DEFAULT_MEMMAP, |acc, memmap| {
-            // TODO: zero init reclaimable regions
-            if let LimineMemoryMapEntryType::Usable
-            // | LimineMemoryMapEntryType::AcpiReclaimable
-            // | LimineMemoryMapEntryType::BootloaderReclaimable
-            = memmap.typ
-            {
-                acc.base = memmap.base.min(acc.base);
-                acc.len += memmap.len;
-                Some(None)
-            } else if acc.len == 0 {
-                acc.base = u64::MAX;
-                Some(None)
-            } else {
-                Some(Some(core::mem::replace(acc, DEFAULT_MEMMAP)))
-            }
-        })
-        .flatten()
-}
+    memiter().filter_map(move |memmap| {
+        // TODO: zero init reclaimable regions
+
+        if first_time && memmap.typ != LimineMemoryMapEntryType::Reserved {
+            trace!("{memmap:?}");
+        }
 
-pub fn memtotal() -> u64 {
-    static TOTAL: Lazy<u64> = Lazy::new(|| {
-        memiter()
-            .filter(|memmap| {
-                memmap.typ != LimineMemoryMapEntryType::Reserved
-                    && memmap.typ != LimineMemoryMapEntryType::Framebuffer
-            })
-            .map(|memmap| memmap.len)
-            .sum::<u64>()
-    });
-    *TOTAL
+        let ty = match memmap.typ {
+            LimineMemoryMapEntryType::Usable => Memtype::Usable,
+            LimineMemoryMapEntryType::BootloaderReclaimable => Memtype::BootloaderReclaimable,
+            LimineMemoryMapEntryType::KernelAndModules => Memtype::KernelAndModules,
+            _ => return None,
+        };
+
+        Some(Memmap {
+            base: PhysAddr::new(memmap.base),
+            len: memmap.len,
+            ty,
+        })
+    })
 }
 
 fn memiter() -> impl Iterator<Item = &'static NonNullPtr<LimineMemmapEntry>> {
diff --git a/src/boot/limine/mod.rs b/src/boot/limine/mod.rs
index a028899..f2d6c0d 100644
--- a/src/boot/limine/mod.rs
+++ b/src/boot/limine/mod.rs
@@ -3,10 +3,10 @@ use crate::{arch, kernel_main};
 
 //
 
+pub use addr::hhdm_offset;
 pub use addr::phys_addr;
 pub use addr::virt_addr;
 pub use mem::memmap;
-pub use mem::memtotal;
 pub use term::_print;
 
 //
diff --git a/src/main.rs b/src/main.rs
index 7deb831..c38b63d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -57,45 +57,14 @@ fn kernel_main() -> ! {
     debug!("Cmdline: {:?}", env::Arguments::get());
 
     debug!(
-        "Kernel addr: {:?} {:?}",
+        "Kernel addr: {:?}, {:?}, HDDM Offset: {:#0X?}",
         boot::virt_addr(),
-        boot::phys_addr()
+        boot::phys_addr(),
+        boot::hhdm_offset()
     );
 
     mem::init();
 
-    /* let (l4, _) = Cr3::read();
-
-    let read_pt = |frame: PhysFrame| -> &mut PageTable {
-        let addr = VirtAddr::new(frame.start_address().as_u64());
-        let table: *mut PageTable = addr.as_mut_ptr();
-        unsafe { &mut *table }
-    };
-
-    for (i, e) in read_pt(l4).iter().enumerate() {
-        if !e.is_unused() {
-            println!("L4 entry {i}: {e:?}");
-
-            for (i, e) in read_pt(e.frame().unwrap()).iter().enumerate() {
-                if !e.is_unused() {
-                    println!("  L3 entry {i}: {e:?}");
-
-                    for (i, e) in read_pt(e.frame().unwrap()).iter().enumerate() {
-                        if !e.is_unused() {
-                            println!("    L2 entry {i}: {e:?}");
-
-                            for (i, e) in read_pt(e.frame().unwrap()).iter().enumerate() {
-                                if !e.is_unused() {
-                                    println!("      L1 entry {i}: {e:?}");
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    } */
-
     // ofc. every kernel has to have this cringy ascii name splash
     info!("\n{}\n", include_str!("./splash"));
 
diff --git a/src/mem/bump.rs b/src/mem/bump.rs
index cd0b47c..def5fef 100644
--- a/src/mem/bump.rs
+++ b/src/mem/bump.rs
@@ -1,4 +1,4 @@
-use super::map::Memmap;
+use super::{map::Memmap, to_higher_half};
 use crate::{boot, error};
 use core::{
     alloc::{GlobalAlloc, Layout},
@@ -57,11 +57,11 @@ unsafe impl GlobalAlloc for BumpAllocator {
         let memory = inner.map.base;
         let mut remaining = inner.remaining.lock();
 
-        let top = memory + *remaining;
+        let top = (memory + *remaining).as_u64();
         let Some(tmp) = top.checked_sub(layout.size() as u64) else {
             error!("OUT OF MEMORY");
             error!(
-                "ALLOC: size: {} align: {} top: {top} memory: {memory} remaining: {remaining}",
+                "ALLOC: size: {} align: {} top: {top} memory: {memory:?} remaining: {remaining}",
                 layout.size(),
                 layout.align()
                 );
@@ -72,11 +72,11 @@ unsafe impl GlobalAlloc for BumpAllocator {
 
         if let Some(left) = remaining.checked_sub(reservation) {
             *remaining = left;
-            (memory + left) as _
+            to_higher_half(memory + left).as_mut_ptr()
         } else {
             error!("OUT OF MEMORY");
             error!(
-                "ALLOC: size: {} align: {} top: {top} new: {new_top} memory: {memory} remaining: {remaining}",
+                "ALLOC: size: {} align: {} top: {top} new: {new_top} memory: {memory:?} remaining: {remaining}",
                 layout.size(),
                 layout.align()
             );
diff --git a/src/mem/map.rs b/src/mem/map.rs
index 6681f94..eb55abe 100644
--- a/src/mem/map.rs
+++ b/src/mem/map.rs
@@ -1,7 +1,73 @@
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+use x86_64::PhysAddr;
+
+//
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub struct Memmap {
-    pub base: u64,
+    pub base: PhysAddr,
     pub len: u64,
+    pub ty: Memtype,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Memtype {
+    Usable,
+    BootloaderReclaimable,
+    KernelAndModules,
+}
+
+//
+
+impl Memmap {
+    /// Returns `true` if the memtype is [`Usable`].
+    ///
+    /// [`Usable`]: Memtype::Usable
+    #[must_use]
+    pub fn is_usable(&self) -> bool {
+        self.ty.is_usable()
+    }
+
+    /// Returns `true` if the memtype is [`BootloaderReclaimable`].
+    ///
+    /// [`BootloaderReclaimable`]: Memtype::BootloaderReclaimable
+    #[must_use]
+    pub fn is_bootloader_reclaimable(&self) -> bool {
+        self.ty.is_bootloader_reclaimable()
+    }
+
+    /// Returns `true` if the memtype is [`KernelAndModules`].
+    ///
+    /// [`KernelAndModules`]: Memtype::KernelAndModules
+    #[must_use]
+    pub fn is_kernel_and_modules(&self) -> bool {
+        self.ty.is_kernel_and_modules()
+    }
+}
+
+impl Memtype {
+    /// Returns `true` if the memtype is [`Usable`].
+    ///
+    /// [`Usable`]: Memtype::Usable
+    #[must_use]
+    pub fn is_usable(&self) -> bool {
+        matches!(self, Self::Usable)
+    }
+
+    /// Returns `true` if the memtype is [`BootloaderReclaimable`].
+    ///
+    /// [`BootloaderReclaimable`]: Memtype::BootloaderReclaimable
+    #[must_use]
+    pub fn is_bootloader_reclaimable(&self) -> bool {
+        matches!(self, Self::BootloaderReclaimable)
+    }
+
+    /// Returns `true` if the memtype is [`KernelAndModules`].
+    ///
+    /// [`KernelAndModules`]: Memtype::KernelAndModules
+    #[must_use]
+    pub fn is_kernel_and_modules(&self) -> bool {
+        matches!(self, Self::KernelAndModules)
+    }
 }
 
 //
diff --git a/src/mem/mod.rs b/src/mem/mod.rs
index 08297bb..8a01c6e 100644
--- a/src/mem/mod.rs
+++ b/src/mem/mod.rs
@@ -1,4 +1,9 @@
 use crate::{boot, debug, mem::map::Memmap, util::fmt::NumberPostfix};
+use x86_64::{
+    registers::control::Cr3,
+    structures::paging::{page_table::FrameError, PageTable, PhysFrame, Size2MiB, Size4KiB},
+    PhysAddr, VirtAddr,
+};
 
 //
 
@@ -6,16 +11,87 @@ pub mod map;
 
 // allocator
 pub mod bump;
-pub mod pfa;
+pub mod pmm;
 
 //
 
 pub fn init() {
-    let usable = boot::memmap().map(|Memmap { len, .. }| len).sum::<u64>();
-    let total = boot::memtotal();
-    debug!("Usable system memory: {}B", usable.postfix_binary());
-    debug!("Total system memory: {}B", total.postfix_binary());
-
     bump::init();
-    pfa::init();
+    pmm::init();
+}
+
+//
+
+fn is_higher_half(addr: u64) -> bool {
+    addr >= boot::hhdm_offset()
+}
+
+fn to_higher_half(addr: PhysAddr) -> VirtAddr {
+    let addr = addr.as_u64();
+    if is_higher_half(addr) {
+        VirtAddr::new(addr)
+    } else {
+        VirtAddr::new(addr + boot::hhdm_offset())
+    }
+}
+
+fn from_higher_half(addr: VirtAddr) -> PhysAddr {
+    let addr = addr.as_u64();
+    if is_higher_half(addr) {
+        PhysAddr::new(addr - boot::hhdm_offset())
+    } else {
+        PhysAddr::new(addr)
+    }
+}
+
+fn walk_page_tables(addr: VirtAddr) -> Option<PhysAddr> {
+    enum AnyPhysFrame {
+        Size4KiB(PhysFrame<Size4KiB>),
+        Size2MiB(PhysFrame<Size2MiB>),
+    }
+
+    impl AnyPhysFrame {
+        fn start_address(&self) -> PhysAddr {
+            match self {
+                AnyPhysFrame::Size4KiB(v) => v.start_address(),
+                AnyPhysFrame::Size2MiB(v) => v.start_address(),
+            }
+        }
+    }
+
+    let (l4, _) = Cr3::read();
+
+    let page_table_indices = [
+        addr.p4_index(),
+        addr.p3_index(),
+        addr.p2_index(),
+        addr.p1_index(),
+    ];
+    let mut frame = AnyPhysFrame::Size4KiB(l4);
+
+    for index in page_table_indices {
+        let virt = to_higher_half(frame.start_address());
+        let table: *const PageTable = virt.as_ptr();
+        let table = unsafe { &*table };
+
+        let entry = &table[index];
+        frame = match entry.frame() {
+            Ok(frame) => AnyPhysFrame::Size4KiB(frame),
+            Err(FrameError::FrameNotPresent) => return None,
+            Err(FrameError::HugeFrame) => {
+                AnyPhysFrame::Size2MiB(PhysFrame::<Size2MiB>::containing_address(entry.addr()))
+            }
+        }
+    }
+
+    Some(frame.start_address() + u64::from(addr.page_offset()))
+}
+
+fn debug_phys_addr(addr: PhysAddr) {
+    debug!(
+        "{:?} {:?} {:?}",
+        addr,
+        walk_page_tables(VirtAddr::new(addr.as_u64())),
+        walk_page_tables(to_higher_half(addr))
+    );
 }
diff --git a/src/mem/pfa.rs b/src/mem/pfa.rs
deleted file mode 100755
index b9241c9..0000000
--- a/src/mem/pfa.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-use super::map::Memmap;
-use crate::{
-    boot, debug,
-    mem::bump,
-    util::{bitmap::Bitmap, fmt::NumberPostfix},
-};
-use core::slice;
-use spin::{Mutex, Once};
-use x86_64::{align_down, align_up};
-
-//
-
-const PAGE_SIZE: u64 = 2u64.pow(12); // 4KiB pages
-
-// const PAGE_SIZE: u64 = 2u64.pow(21); // 2MiB pages
-
-static PFA: Once<Mutex<PageFrameAllocator>> = Once::new();
-
-//
-
-pub fn init() {
-    let mem_bottom = boot::memmap()
-        .map(|Memmap { base, .. }| base)
-        .min()
-        .expect("No memory");
-
-    let mem_top = boot::memmap()
-        .map(|Memmap { base, len }| base + len)
-        .max()
-        .expect("No memory");
-
-    // size in bytes
-    let bitmap_size = (mem_top - mem_bottom) / PAGE_SIZE / 8 + 1;
-    let bitmap_data = boot::memmap()
-        .find(|Memmap { len, .. }| *len >= bitmap_size)
-        .expect("No place to store PageFrameAllocator bitmap")
-        .base;
-
-    // SAFETY: this bitmap is going to be initialized before it is read from
-    let bitmap = unsafe { slice::from_raw_parts_mut(bitmap_data as _, bitmap_size as _) };
-    let mut bitmap = Bitmap::new(bitmap);
-    bitmap.fill(true); // initialized here
-
-    let bottom_page = align_up(mem_bottom, PAGE_SIZE) / PAGE_SIZE;
-
-    // free up some pages
-    for Memmap { mut base, mut len } in boot::memmap() {
-        if let Some(map) = bump::map() && map.base == base {
-            // skip the BumpAllocator spot
-            base += map.base;
-            len -= map.len;
-        }
-        if base == bitmap_data {
-            // skip the bitmap allocation spot
-            base += bitmap_data;
-            len -= bitmap_size;
-        }
-
-        let mut bottom = align_up(base, PAGE_SIZE);
-        let mut top = align_down(base + len, PAGE_SIZE);
-
-        if bottom >= top {
-            continue;
-        }
-
-        debug!(
-            "Free pages: {:#0X?} ({}B)",
-            bottom..top,
-            (top - bottom).postfix_binary()
-        );
-
-        bottom /= PAGE_SIZE;
-        top /= PAGE_SIZE;
-        bottom -= bottom_page;
-        top -= bottom_page;
-
-        for page in bottom..top {
-            #[cfg(debug_assertions)]
-            bitmap.set(page as _, false).unwrap();
-            #[cfg(not(debug_assertions))]
-            let res = bitmap.set(page as _, false);
-        }
-    }
-
-    let free = bitmap.iter_false().count() as u64 * PAGE_SIZE;
-    let used = 0;
-    debug!("Free pages: ({}B)", free.postfix_binary());
-
-    PFA.call_once(|| {
-        Mutex::new(PageFrameAllocator {
-            bitmap,
-            free,
-            used,
-            bottom_page,
-        })
-    });
-}
-
-//
-
-pub struct PageFrameAllocator {
-    bitmap: Bitmap<'static>,
-    free: u64,
-    used: u64,
-    bottom_page: u64,
-}
-
-//
-
-impl PageFrameAllocator {
-    pub fn free_page(&mut self, _addr: u64) {}
-}
diff --git a/src/mem/pmm.rs b/src/mem/pmm.rs
new file mode 100755
index 0000000..413dc6c
--- /dev/null
+++ b/src/mem/pmm.rs
@@ -0,0 +1,328 @@
+use super::{map::Memmap, to_higher_half};
+use crate::{
+    boot, debug,
+    log::{test_log_level, LogLevel},
+    mem::{bump, map::Memtype},
+    trace,
+    util::{bitmap::Bitmap, fmt::NumberPostfix},
+};
+use core::{
+    any::type_name,
+    convert::identity,
+    fmt, slice,
+    sync::atomic::{AtomicU64, AtomicUsize, Ordering},
+};
+use spin::{Mutex, Once};
+use x86_64::{align_down, align_up, PhysAddr, VirtAddr};
+
+//
+
+const PAGE_SIZE: u64 = 2u64.pow(12); // 4KiB pages
+
+// const PAGE_SIZE: u64 = 2u64.pow(21); // 2MiB pages
+
+static PFA: Once<PageFrameAllocator> = Once::new();
+
+//
+
+pub fn init() {
+    // usable system memory
+    let usable: u64 = boot::memmap()
+        .filter(Memmap::is_usable)
+        .map(|Memmap { len, .. }| len)
+        .sum();
+
+    // total system memory
+    let total: u64 = boot::memmap().map(|Memmap { len, .. }| len).sum();
+
+    // the end of the usable physical memory address space
+    let top = boot::memmap()
+        .filter(Memmap::is_usable)
+        .map(|Memmap { base, len, ty }| base + len)
+        .max()
+        .expect("No memory");
+
+    // size in bytes
+    let bitmap_size = align_up(top.as_u64() / PAGE_SIZE / 8, PAGE_SIZE);
+    let bitmap_data = boot::memmap()
+        .filter(Memmap::is_usable)
+        .find(|Memmap { len, .. }| *len >= bitmap_size)
+        .expect("No place to store PageFrameAllocator bitmap")
+        .base;
+
+    // SAFETY: this bitmap is going to be initialized before it is read from
+    let bitmap = unsafe {
+        slice::from_raw_parts_mut(to_higher_half(bitmap_data).as_mut_ptr(), bitmap_size as _)
+    };
+    let mut bitmap = Bitmap::new(bitmap);
+    bitmap.fill(true); // initialized here
+
+    // free up some pages
+    for Memmap {
+        mut base,
+        mut len,
+        ty,
+    } in boot::memmap().filter(Memmap::is_usable)
+    {
+        if let Some(map) = bump::map() && map.base == base {
+            // skip the BumpAllocator spot
+            base += map.base.as_u64();
+            len -= map.len;
+        }
+        if base == bitmap_data {
+            // skip the bitmap allocation spot
+            base += bitmap_data.as_u64();
+            len -= bitmap_size;
+        }
+
+        let mut bottom = base.as_u64();
+        let mut top = base.as_u64() + len;
+
+        debug!(
+            "Free pages: {:#0X?} ({}B)",
+            bottom..top,
+            (top - bottom).postfix_binary()
+        );
+
+        bottom /= PAGE_SIZE;
+        top /= PAGE_SIZE;
+
+        for page in bottom..top {
+            bitmap.set(page as _, false).unwrap();
+        }
+    }
+
+    let used = bump::map().map(|Memmap { len, .. }| len).unwrap_or(0) + bitmap_size;
+
+    let pfa = PageFrameAllocator {
+        bitmap: bitmap.into(),
+        usable: usable.into(),
+        used: used.into(),
+        total: total.into(),
+
+        last_alloc_index: 0.into(),
+    };
+
+    debug!("PFA:\n{pfa}");
+
+    PFA.call_once(|| pfa);
+}
+
+//
+
+pub struct PageFrameAllocator {
+    // 1 bits are used pages
+    bitmap: Mutex<Bitmap<'static>>,
+    usable: AtomicU64,
+    used: AtomicU64,
+    total: AtomicU64,
+
+    last_alloc_index: AtomicUsize,
+}
+
+#[derive(Debug)]
+pub struct PageFrame {
+    first: PhysAddr,
+    count: usize,
+}
+
+//
+
+impl PageFrameAllocator {
+    /// System total memory in bytes
+    pub fn total_mem(&self) -> u64 {
+        self.total.load(Ordering::SeqCst)
+    }
+
+    /// System usable memory in bytes
+    pub fn usable_mem(&self) -> u64 {
+        self.usable.load(Ordering::SeqCst)
+    }
+
+    /// Currently used usable memory in bytes
+    pub fn used_mem(&self) -> u64 {
+        self.used.load(Ordering::SeqCst)
+    }
+
+    /// Currently free usable memory in bytes
+    pub fn free_mem(&self) -> u64 {
+        self.usable_mem() - self.used_mem()
+    }
+
+    /// Reserved memory in bytes
+    pub fn reserved_mem(&self) -> u64 {
+        self.total_mem() - self.usable_mem()
+    }
+
+    /// Free up pages
+    ///
+    /// Double frees are not possible due to [`PageFrame`] missing [`Clone`] and it cannot be
+    /// constructed manually
+    pub fn free(&self, frame: PageFrame) {
+        if frame.first.as_u64() == 0 || frame.count == 0 {
+            return;
+        }
+
+        let mut bitmap = self.bitmap.lock();
+        let page = (frame.first.as_u64() / PAGE_SIZE) as usize;
+        for page in page..page + frame.count {
+            bitmap.set(page, false).unwrap();
+        }
+
+        self.used
+            .fetch_sub(frame.count as u64 * PAGE_SIZE, Ordering::SeqCst);
+    }
+
+    /// Alloc pages
+    ///
+    /// Use [`Self::free`] to not leak pages (-> memory)
+    pub fn alloc(&self, count: usize) -> PageFrame {
+        if count == 0 {
+            return PageFrame {
+                first: PhysAddr::new(0),
+                count: 0,
+            };
+        }
+
+        let mut bitmap = self.bitmap.lock();
+
+        let first_page = self.alloc_at(&mut bitmap, count).unwrap_or_else(|| {
+            // TODO: handle OOM a bit better
+            self.alloc_from(0);
+            self.alloc_at(&mut bitmap, count).expect("OOM")
+        });
+
+        self.alloc_from(first_page + count);
+
+        let addr = PhysAddr::new(first_page as u64 * PAGE_SIZE);
+
+        // SAFETY: TODO:
+        let page_data: &mut [u8] = unsafe {
+            slice::from_raw_parts_mut(
+                to_higher_half(addr).as_mut_ptr(),
+                count * PAGE_SIZE as usize,
+            )
+        };
+
+        // fill the page with zeros
+        trace!("Memzeroing {:?}", page_data.as_ptr_range());
+        page_data.fill(0);
+
+        self.used
+            .fetch_add(count as u64 * PAGE_SIZE, Ordering::SeqCst);
+
+        PageFrame { first: addr, count }
+    }
+
+    fn alloc_from(&self, index: usize) {
+        self.last_alloc_index.store(index, Ordering::SeqCst)
+    }
+
+    // returns the page index, not the page address
+    fn alloc_at(&self, bitmap: &mut Bitmap, count: usize) -> Option<usize> {
+        let mut first_page = self.last_alloc_index.load(Ordering::SeqCst);
+        'main: loop {
+            if first_page + count > bitmap.len() {
+                return None;
+            }
+
+            /* if test_log_level(LogLevel::Trace) {
+                trace!(
+                    "Trying to allocate {count} pages from {:?}",
+                    to_higher_half(PhysAddr::new(first_page as u64 * PAGE_SIZE))
+                );
+            } */
+
+            // go reversed so that skips would be more efficient
+            for offs in (0..count).rev() {
+                // SAFETY: `first_page + offs` < `first_page + count` <= `bitmap.len()`
+                // => bitmap has to contain `first_page + offs`
+                if unsafe { bitmap.get(first_page + offs).unwrap_unchecked() } {
+                    // skip all page windows which have this locked page
+                    first_page = first_page + offs + 1;
+                    continue 'main;
+                }
+            }
+
+            // found a window of free pages
+            for offs in 0..count {
+                // lock them
+                _ = bitmap.set(first_page + offs, true);
+            }
+
+            return Some(first_page);
+        }
+    }
+}
+
+impl fmt::Display for PageFrameAllocator {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        writeln!(
+            f,
+            "Total system memory: {}B",
+            self.total_mem().postfix_binary()
+        )?;
+        writeln!(
+            f,
+            "Usable system memory: {}B",
+            self.usable_mem().postfix_binary()
+        )?;
+        writeln!(
+            f,
+            "Used system memory: {}B",
+            self.used_mem().postfix_binary()
+        )?;
+        writeln!(
+            f,
+            "Free system memory: {}B",
+            self.free_mem().postfix_binary()
+        )?;
+        write!(
+            f,
+            "Reserved system memory: {}B",
+            self.reserved_mem().postfix_binary()
+        )?;
+
+        Ok(())
+    }
+}
+
+impl PageFrame {
+    pub fn addr(&self) -> PhysAddr {
+        self.first
+    }
+}
+
+//
+
+#[cfg(test)]
+mod tests {
+    use crate::mem::pmm::PFA;
+
+    #[test_case]
+    fn pfa_simple() {
+        let pfa = PFA.get().unwrap();
+
+        let a = pfa.alloc(1);
+        assert_ne!(a.addr().as_u64(), 0);
+
+        let b = pfa.alloc(1);
+        assert_ne!(b.addr().as_u64(), 0);
+        assert_ne!(a.addr().as_u64(), b.addr().as_u64());
+
+        pfa.free(a);
+        pfa.alloc_from(0);
+        let c = pfa.alloc(1);
+        assert_ne!(c.addr().as_u64(), 0);
+        assert_ne!(b.addr().as_u64(), c.addr().as_u64());
+
+        let d = pfa.alloc(1);
+        assert_ne!(d.addr().as_u64(), 0);
+        assert_ne!(c.addr().as_u64(), d.addr().as_u64());
+
+        // pfa.free(a); // <- compile error as expected
+        pfa.free(b);
+        pfa.free(c);
+        pfa.free(d);
+    }
+}
diff --git a/src/video/font.rs b/src/video/font.rs
index 1224e22..2a9bbdb 100644
--- a/src/video/font.rs
+++ b/src/video/font.rs
@@ -1,258 +1,1113 @@
 pub static FONT: [([u16; 16], bool); 256] = [
-	([21845, 32768, 1, 32768, 1, 35410, 2647, 35418, 31123, 32768, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 51612, 19011, 64076, 19025, 51598, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 42460, 9347, 39052, 9361, 42126, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 42462, 9347, 39070, 9347, 42142, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 64926, 4675, 37470, 4675, 37278, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 45662, 19011, 51934, 27459, 62046, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 42380, 5203, 35934, 5203, 42386, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 35790, 2131, 35790, 2131, 64462, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 40056, 649, 35960, 4233, 36472, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 40772, 1093, 33916, 1093, 33860, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 40708, 261, 40708, 261, 33148, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 40772, 1093, 33860, 1065, 33808, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 40828, 261, 40828, 261, 33028, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 36728, 4357, 36612, 2309, 37240, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 36472, 4357, 37176, 4417, 36412, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 40824, 1029, 33848, 1089, 40764, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 47260, 2213, 47268, 2213, 48028, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 37262, 6227, 36946, 4179, 47502, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 39310, 8275, 36946, 2131, 47502, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 39310, 8275, 38994, 8275, 39310, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 41358, 12371, 43090, 14419, 41358, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 51602, 10839, 39510, 11227, 51794, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 51756, 22851, 59532, 18577, 51342, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 40414, 9347, 40094, 9347, 40094, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 42188, 11555, 46562, 9507, 42284, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 37244, 6917, 38268, 4357, 37244, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 47708, 19011, 47692, 19025, 47502, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 62366, 2115, 35230, 2563, 61918, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 36472, 265, 34424, 2057, 34568, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 36464, 265, 34408, 2121, 34672, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 36408, 329, 34360, 2089, 34632, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 36424, 329, 34376, 2121, 34608, 1, 32768, 1, 32768, 1, 43690], true),
-	([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, 0, 16, 16, 0, 0], false),
-	([0, 0, 68, 68, 68, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 72, 72, 72, 126, 36, 36, 126, 18, 18, 18, 0, 0], false),
-	([0, 0, 0, 0, 16, 124, 146, 18, 28, 112, 144, 146, 124, 16, 0, 0], false),
-	([0, 0, 0, 0, 140, 82, 82, 44, 16, 16, 104, 148, 148, 98, 0, 0], false),
-	([0, 0, 0, 0, 56, 68, 68, 40, 24, 148, 162, 66, 98, 156, 0, 0], false),
-	([0, 0, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 32, 16, 16, 8, 8, 8, 8, 8, 8, 16, 16, 32, 0], false),
-	([0, 0, 0, 4, 8, 8, 16, 16, 16, 16, 16, 16, 8, 8, 4, 0], false),
-	([0, 0, 0, 0, 0, 0, 16, 146, 84, 56, 84, 146, 16, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 16, 16, 16, 254, 16, 16, 16, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 16, 16, 8], false),
-	([0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 0, 0], false),
-	([0, 0, 0, 0, 64, 64, 32, 16, 16, 8, 8, 4, 2, 2, 0, 0], false),
-	([0, 0, 0, 0, 24, 36, 66, 98, 82, 74, 70, 66, 36, 24, 0, 0], false),
-	([0, 0, 0, 0, 16, 24, 20, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 64, 48, 8, 4, 2, 2, 126, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 64, 56, 64, 64, 66, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 32, 48, 40, 36, 34, 34, 126, 32, 32, 32, 0, 0], false),
-	([0, 0, 0, 0, 126, 2, 2, 2, 62, 64, 64, 64, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 56, 4, 2, 2, 62, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 126, 64, 64, 32, 32, 32, 16, 16, 16, 16, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 66, 60, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 66, 124, 64, 64, 64, 32, 28, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 24, 24, 0, 0, 0, 24, 24, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 24, 24, 0, 0, 0, 24, 16, 16, 8, 0], false),
-	([0, 0, 0, 0, 0, 64, 32, 16, 8, 4, 8, 16, 32, 64, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 0, 126, 0, 0, 0, 126, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 2, 4, 8, 16, 32, 16, 8, 4, 2, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 64, 32, 16, 16, 0, 16, 16, 0, 0], false),
-	([0, 0, 0, 0, 56, 68, 82, 106, 74, 74, 74, 114, 4, 120, 0, 0], false),
-	([0, 0, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0], false),
-	([0, 0, 0, 0, 62, 66, 66, 66, 62, 66, 66, 66, 66, 62, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 2, 2, 2, 2, 66, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 30, 34, 66, 66, 66, 66, 66, 66, 34, 30, 0, 0], false),
-	([0, 0, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 126, 0, 0], false),
-	([0, 0, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 2, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 2, 2, 114, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 0, 0, 66, 66, 66, 66, 126, 66, 66, 66, 66, 66, 0, 0], false),
-	([0, 0, 0, 0, 124, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([0, 0, 0, 0, 248, 32, 32, 32, 32, 32, 32, 34, 34, 28, 0, 0], false),
-	([0, 0, 0, 0, 66, 34, 18, 10, 6, 6, 10, 18, 34, 66, 0, 0], false),
-	([0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 126, 0, 0], false),
-	([0, 0, 0, 0, 66, 66, 102, 102, 90, 90, 66, 66, 66, 66, 0, 0], false),
-	([0, 0, 0, 0, 66, 70, 70, 74, 74, 82, 82, 98, 98, 66, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 62, 66, 66, 66, 62, 2, 2, 2, 2, 2, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 66, 66, 66, 66, 90, 102, 60, 192, 0], false),
-	([0, 0, 0, 0, 62, 66, 66, 66, 62, 18, 34, 34, 66, 66, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 2, 12, 48, 64, 66, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 254, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 0], false),
-	([0, 0, 0, 0, 66, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 130, 130, 130, 68, 68, 68, 40, 40, 16, 16, 0, 0], false),
-	([0, 0, 0, 0, 66, 66, 66, 66, 90, 90, 102, 102, 66, 66, 0, 0], false),
-	([0, 0, 0, 0, 66, 66, 36, 36, 24, 24, 36, 36, 66, 66, 0, 0], false),
-	([0, 0, 0, 0, 130, 130, 68, 68, 40, 16, 16, 16, 16, 16, 0, 0], false),
-	([0, 0, 0, 0, 126, 64, 64, 32, 16, 8, 4, 2, 2, 126, 0, 0], false),
-	([0, 0, 0, 112, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 112, 0], false),
-	([0, 0, 0, 0, 2, 2, 4, 8, 8, 16, 16, 32, 64, 64, 0, 0], false),
-	([0, 0, 0, 14, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 14, 0], false),
-	([0, 0, 24, 36, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 0], false),
-	([0, 4, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 0, 2, 2, 2, 58, 70, 66, 66, 66, 66, 70, 58, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 60, 66, 2, 2, 2, 2, 66, 60, 0, 0], false),
-	([0, 0, 0, 64, 64, 64, 92, 98, 66, 66, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 60, 66, 66, 126, 2, 2, 66, 60, 0, 0], false),
-	([0, 0, 0, 48, 8, 8, 8, 62, 8, 8, 8, 8, 8, 8, 0, 0], false),
-	([0, 0, 0, 0, 0, 64, 92, 34, 34, 34, 28, 4, 60, 66, 66, 60], false),
-	([0, 0, 0, 2, 2, 2, 58, 70, 66, 66, 66, 66, 66, 66, 0, 0], false),
-	([0, 0, 0, 16, 16, 0, 24, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([0, 0, 0, 32, 32, 0, 48, 32, 32, 32, 32, 32, 32, 32, 18, 12], false),
-	([0, 0, 0, 2, 2, 2, 34, 18, 10, 6, 10, 18, 34, 66, 0, 0], false),
-	([0, 0, 0, 24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 110, 146, 146, 146, 146, 146, 146, 146, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 58, 70, 66, 66, 66, 66, 66, 66, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 58, 70, 66, 66, 66, 66, 70, 58, 2, 2], false),
-	([0, 0, 0, 0, 0, 0, 92, 98, 66, 66, 66, 66, 98, 92, 64, 64], false),
-	([0, 0, 0, 0, 0, 0, 58, 70, 66, 2, 2, 2, 2, 2, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 60, 66, 2, 12, 48, 64, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 8, 8, 8, 62, 8, 8, 8, 8, 8, 48, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 66, 66, 66, 66, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 66, 66, 66, 36, 36, 36, 24, 24, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 130, 146, 146, 146, 146, 146, 146, 108, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 66, 66, 36, 24, 24, 36, 66, 66, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 66, 66, 66, 66, 66, 100, 88, 64, 64, 60], false),
-	([0, 0, 0, 0, 0, 0, 126, 64, 32, 16, 8, 4, 2, 126, 0, 0], false),
-	([0, 0, 0, 48, 8, 8, 16, 16, 8, 4, 8, 16, 16, 8, 8, 48], false),
-	([0, 0, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16], false),
-	([0, 0, 0, 12, 16, 16, 8, 8, 16, 32, 16, 8, 8, 16, 16, 12], false),
-	([0, 0, 0, 140, 146, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([21845, 32768, 1, 32768, 1, 35790, 2131, 35794, 2131, 64462, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 47502, 19027, 52174, 19011, 47682, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 47506, 19027, 47710, 2643, 35218, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 51662, 19027, 63950, 18515, 51278, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 51666, 19031, 63962, 19027, 51666, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 40238, 9573, 42404, 9509, 40238, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 35794, 2135, 35802, 2131, 64466, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 45980, 18499, 63884, 18961, 51662, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 45982, 18499, 63902, 18947, 51678, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 47570, 1171, 39070, 8339, 40082, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 48036, 8485, 41276, 8485, 39204, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 45986, 2339, 37154, 8469, 39176, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 47182, 18515, 51278, 18499, 48066, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 51278, 18515, 51278, 18499, 46018, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 33904, 1169, 33904, 1105, 33936, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 48028, 16451, 45452, 2577, 63950, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 48028, 16451, 45452, 16913, 47566, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 62350, 2131, 45138, 16467, 48014, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 41550, 12883, 41550, 8771, 61826, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 47694, 16979, 45646, 2627, 63874, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 47580, 1155, 39052, 8337, 40078, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 52124, 18499, 63554, 18499, 52124, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 37188, 4461, 38228, 6981, 37188, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 45532, 19011, 63948, 18513, 51278, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 45534, 19011, 63966, 18499, 51294, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 61852, 2627, 45644, 16977, 47502, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 55756, 17443, 50596, 17705, 55750, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 48028, 4163, 36940, 4177, 48014, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 48028, 4163, 37250, 4611, 47580, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 40816, 1033, 33840, 1089, 33848, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 62348, 2131, 35218, 2579, 61900, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 37176, 6985, 38200, 4361, 37128, 1, 32768, 1, 32768, 1, 43690], true),
-	([21845, 32768, 1, 32768, 1, 61900, 2643, 35294, 2131, 61522, 1, 32768, 1, 32768, 1, 43690], true),
-	([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 16, 16, 0, 16, 16, 16, 16, 16, 16, 16, 0, 0], false),
-	([0, 0, 0, 0, 16, 16, 124, 146, 18, 18, 146, 124, 16, 16, 0, 0], false),
-	([0, 0, 0, 0, 112, 8, 8, 8, 62, 8, 8, 8, 124, 134, 0, 0], false),
-	([0, 0, 0, 0, 0, 66, 60, 36, 66, 66, 36, 60, 66, 0, 0, 0], false),
-	([0, 0, 0, 0, 130, 68, 40, 16, 254, 16, 254, 16, 16, 16, 0, 0], false),
-	([0, 0, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 16, 16, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 2, 60, 66, 66, 60, 64, 66, 60, 0, 0], false),
-	([36, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 153, 165, 133, 133, 165, 153, 66, 60, 0, 0], false),
-	([0, 0, 56, 64, 120, 68, 120, 0, 124, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 72, 72, 36, 36, 18, 36, 36, 72, 72, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 64, 64, 64, 0, 0], false),
-	([21845, 32768, 1, 50780, 10819, 37836, 4689, 37454, 1, 32768, 1, 33728, 1, 32768, 1, 43690], true),
-	([0, 0, 0, 0, 60, 66, 157, 165, 165, 157, 149, 165, 66, 60, 0, 0], false),
-	([0, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 24, 36, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 16, 16, 16, 254, 16, 16, 16, 0, 254, 0, 0, 0], false),
-	([0, 0, 0, 28, 34, 32, 24, 4, 2, 62, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 28, 34, 32, 28, 32, 34, 28, 0, 0, 0, 0, 0, 0], false),
-	([0, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 66, 66, 66, 66, 66, 66, 102, 154, 2, 1], false),
-	([0, 0, 0, 0, 252, 94, 94, 94, 92, 80, 80, 80, 80, 80, 80, 0], false),
-	([0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 12], false),
-	([0, 0, 0, 8, 12, 10, 8, 8, 8, 62, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 56, 68, 68, 68, 56, 0, 124, 0, 0, 0, 0, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 18, 18, 36, 36, 72, 36, 36, 18, 18, 0, 0], false),
-	([0, 0, 0, 0, 68, 70, 36, 20, 20, 72, 104, 84, 114, 66, 0, 0], false),
-	([0, 0, 0, 0, 68, 70, 36, 20, 20, 40, 88, 68, 34, 114, 0, 0], false),
-	([0, 0, 0, 0, 70, 72, 36, 24, 22, 72, 104, 84, 114, 66, 0, 0], false),
-	([0, 0, 0, 0, 8, 8, 0, 8, 8, 4, 2, 66, 66, 60, 0, 0], false),
-	([12, 48, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0], false),
-	([48, 12, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0], false),
-	([24, 36, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0], false),
-	([76, 50, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0], false),
-	([36, 36, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0], false),
-	([24, 36, 24, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0], false),
-	([0, 0, 0, 0, 248, 20, 18, 18, 254, 18, 18, 18, 18, 242, 0, 0], false),
-	([0, 0, 0, 0, 60, 66, 66, 2, 2, 2, 2, 66, 66, 60, 16, 12], false),
-	([12, 48, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 126, 0, 0], false),
-	([48, 12, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 126, 0, 0], false),
-	([24, 36, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 126, 0, 0], false),
-	([36, 36, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 126, 0, 0], false),
-	([24, 96, 0, 0, 124, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([48, 12, 0, 0, 124, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([24, 36, 0, 0, 124, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([36, 36, 0, 0, 124, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([0, 0, 0, 0, 30, 34, 66, 66, 79, 66, 66, 66, 34, 30, 0, 0], false),
-	([76, 50, 0, 0, 66, 70, 70, 74, 74, 82, 82, 98, 98, 66, 0, 0], false),
-	([12, 48, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([48, 12, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([24, 36, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([76, 50, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([36, 36, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 0, 66, 36, 24, 36, 66, 0, 0, 0, 0], false),
-	([0, 0, 0, 64, 92, 34, 98, 82, 82, 74, 74, 70, 68, 58, 2, 0], false),
-	([12, 48, 0, 0, 66, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([48, 12, 0, 0, 66, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([24, 36, 0, 0, 66, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([36, 36, 0, 0, 66, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([48, 12, 0, 0, 130, 130, 68, 68, 40, 16, 16, 16, 16, 16, 0, 0], false),
-	([0, 0, 0, 2, 2, 30, 34, 66, 66, 34, 30, 2, 2, 2, 0, 0], false),
-	([0, 0, 0, 0, 28, 34, 34, 18, 26, 34, 66, 66, 74, 50, 0, 0], false),
-	([0, 0, 12, 48, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 48, 12, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 24, 36, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 76, 50, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 36, 36, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0], false),
-	([0, 24, 36, 24, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 124, 146, 144, 252, 18, 18, 146, 124, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 60, 66, 2, 2, 2, 2, 66, 60, 16, 12], false),
-	([0, 0, 12, 48, 0, 0, 60, 66, 66, 126, 2, 2, 66, 60, 0, 0], false),
-	([0, 0, 48, 12, 0, 0, 60, 66, 66, 126, 2, 2, 66, 60, 0, 0], false),
-	([0, 0, 24, 36, 0, 0, 60, 66, 66, 126, 2, 2, 66, 60, 0, 0], false),
-	([0, 0, 36, 36, 0, 0, 60, 66, 66, 126, 2, 2, 66, 60, 0, 0], false),
-	([0, 0, 12, 48, 0, 0, 24, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([0, 0, 48, 12, 0, 0, 24, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([0, 0, 24, 36, 0, 0, 24, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([0, 0, 36, 36, 0, 0, 24, 16, 16, 16, 16, 16, 16, 124, 0, 0], false),
-	([0, 0, 76, 48, 40, 68, 64, 124, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 76, 50, 0, 0, 58, 70, 66, 66, 66, 66, 66, 66, 0, 0], false),
-	([0, 0, 12, 48, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 48, 12, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 24, 36, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 76, 50, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 36, 36, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0], false),
-	([0, 0, 0, 0, 0, 0, 24, 0, 0, 126, 0, 0, 24, 0, 0, 0], false),
-	([0, 0, 0, 0, 0, 64, 60, 98, 82, 82, 74, 74, 70, 60, 2, 0], false),
-	([0, 0, 12, 48, 0, 0, 66, 66, 66, 66, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 48, 12, 0, 0, 66, 66, 66, 66, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 24, 36, 0, 0, 66, 66, 66, 66, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 36, 36, 0, 0, 66, 66, 66, 66, 66, 66, 98, 92, 0, 0], false),
-	([0, 0, 48, 12, 0, 0, 66, 66, 66, 66, 66, 100, 88, 64, 64, 60], false),
-	([0, 0, 0, 2, 2, 2, 58, 70, 66, 66, 66, 66, 70, 58, 2, 2], false),
-	([0, 0, 36, 36, 0, 0, 66, 66, 66, 66, 66, 100, 88, 64, 64, 60], false),
-];
\ No newline at end of file
+    (
+        [
+            21845, 32768, 1, 32768, 1, 35410, 2647, 35418, 31123, 32768, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 51612, 19011, 64076, 19025, 51598, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 42460, 9347, 39052, 9361, 42126, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 42462, 9347, 39070, 9347, 42142, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 64926, 4675, 37470, 4675, 37278, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 45662, 19011, 51934, 27459, 62046, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 42380, 5203, 35934, 5203, 42386, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 35790, 2131, 35790, 2131, 64462, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 40056, 649, 35960, 4233, 36472, 1, 32768, 1, 32768, 1, 43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 40772, 1093, 33916, 1093, 33860, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 40708, 261, 40708, 261, 33148, 1, 32768, 1, 32768, 1, 43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 40772, 1093, 33860, 1065, 33808, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 40828, 261, 40828, 261, 33028, 1, 32768, 1, 32768, 1, 43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 36728, 4357, 36612, 2309, 37240, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 36472, 4357, 37176, 4417, 36412, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 40824, 1029, 33848, 1089, 40764, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 47260, 2213, 47268, 2213, 48028, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 37262, 6227, 36946, 4179, 47502, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 39310, 8275, 36946, 2131, 47502, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 39310, 8275, 38994, 8275, 39310, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 41358, 12371, 43090, 14419, 41358, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 51602, 10839, 39510, 11227, 51794, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 51756, 22851, 59532, 18577, 51342, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 40414, 9347, 40094, 9347, 40094, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 42188, 11555, 46562, 9507, 42284, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 37244, 6917, 38268, 4357, 37244, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 47708, 19011, 47692, 19025, 47502, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 62366, 2115, 35230, 2563, 61918, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 36472, 265, 34424, 2057, 34568, 1, 32768, 1, 32768, 1, 43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 36464, 265, 34408, 2121, 34672, 1, 32768, 1, 32768, 1, 43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 36408, 329, 34360, 2089, 34632, 1, 32768, 1, 32768, 1, 43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 36424, 329, 34376, 2121, 34608, 1, 32768, 1, 32768, 1, 43690,
+        ],
+        true,
+    ),
+    ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    (
+        [0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, 0, 16, 16, 0, 0],
+        false,
+    ),
+    ([0, 0, 68, 68, 68, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    (
+        [0, 0, 0, 0, 72, 72, 72, 126, 36, 36, 126, 18, 18, 18, 0, 0],
+        false,
+    ),
+    (
+        [
+            0, 0, 0, 0, 16, 124, 146, 18, 28, 112, 144, 146, 124, 16, 0, 0,
+        ],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 140, 82, 82, 44, 16, 16, 104, 148, 148, 98, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 56, 68, 68, 40, 24, 148, 162, 66, 98, 156, 0, 0],
+        false,
+    ),
+    ([0, 0, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    (
+        [0, 0, 0, 32, 16, 16, 8, 8, 8, 8, 8, 8, 16, 16, 32, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 4, 8, 8, 16, 16, 16, 16, 16, 16, 8, 8, 4, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 16, 146, 84, 56, 84, 146, 16, 0, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 16, 16, 16, 254, 16, 16, 16, 0, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 16, 16, 8], false),
+    ([0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0], false),
+    ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 0, 0], false),
+    ([0, 0, 0, 0, 64, 64, 32, 16, 16, 8, 8, 4, 2, 2, 0, 0], false),
+    (
+        [0, 0, 0, 0, 24, 36, 66, 98, 82, 74, 70, 66, 36, 24, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 16, 24, 20, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 64, 48, 8, 4, 2, 2, 126, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 64, 56, 64, 64, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 32, 48, 40, 36, 34, 34, 126, 32, 32, 32, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 126, 2, 2, 2, 62, 64, 64, 64, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 56, 4, 2, 2, 62, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 126, 64, 64, 32, 32, 32, 16, 16, 16, 16, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 66, 60, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 66, 124, 64, 64, 64, 32, 28, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 0, 0, 24, 24, 0, 0, 0, 24, 24, 0, 0, 0], false),
+    ([0, 0, 0, 0, 0, 0, 24, 24, 0, 0, 0, 24, 16, 16, 8, 0], false),
+    (
+        [0, 0, 0, 0, 0, 64, 32, 16, 8, 4, 8, 16, 32, 64, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 0, 0, 0, 126, 0, 0, 0, 126, 0, 0, 0, 0], false),
+    ([0, 0, 0, 0, 0, 2, 4, 8, 16, 32, 16, 8, 4, 2, 0, 0], false),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 64, 32, 16, 16, 0, 16, 16, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 56, 68, 82, 106, 74, 74, 74, 114, 4, 120, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 62, 66, 66, 66, 62, 66, 66, 66, 66, 62, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 2, 2, 2, 2, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 30, 34, 66, 66, 66, 66, 66, 66, 34, 30, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 126, 0, 0], false),
+    ([0, 0, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 2, 0, 0], false),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 2, 2, 114, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 66, 66, 66, 66, 126, 66, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 124, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 248, 32, 32, 32, 32, 32, 32, 34, 34, 28, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 66, 34, 18, 10, 6, 6, 10, 18, 34, 66, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 126, 0, 0], false),
+    (
+        [0, 0, 0, 0, 66, 66, 102, 102, 90, 90, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 66, 70, 70, 74, 74, 82, 82, 98, 98, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 62, 66, 66, 66, 62, 2, 2, 2, 2, 2, 0, 0], false),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 66, 66, 66, 66, 90, 102, 60, 192, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 62, 66, 66, 66, 62, 18, 34, 34, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 2, 12, 48, 64, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 254, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 66, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 130, 130, 130, 68, 68, 68, 40, 40, 16, 16, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 66, 66, 66, 66, 90, 90, 102, 102, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 66, 66, 36, 36, 24, 24, 36, 36, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 130, 130, 68, 68, 40, 16, 16, 16, 16, 16, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 126, 64, 64, 32, 16, 8, 4, 2, 2, 126, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 112, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 112, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 2, 2, 4, 8, 8, 16, 16, 32, 64, 64, 0, 0], false),
+    ([0, 0, 0, 14, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 14, 0], false),
+    ([0, 0, 24, 36, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 0], false),
+    ([0, 4, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    (
+        [0, 0, 0, 0, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 2, 2, 2, 58, 70, 66, 66, 66, 66, 70, 58, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 0, 0, 60, 66, 2, 2, 2, 2, 66, 60, 0, 0], false),
+    (
+        [0, 0, 0, 64, 64, 64, 92, 98, 66, 66, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 60, 66, 66, 126, 2, 2, 66, 60, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 48, 8, 8, 8, 62, 8, 8, 8, 8, 8, 8, 0, 0], false),
+    (
+        [0, 0, 0, 0, 0, 64, 92, 34, 34, 34, 28, 4, 60, 66, 66, 60],
+        false,
+    ),
+    (
+        [0, 0, 0, 2, 2, 2, 58, 70, 66, 66, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 16, 16, 0, 24, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 32, 32, 0, 48, 32, 32, 32, 32, 32, 32, 32, 18, 12],
+        false,
+    ),
+    (
+        [0, 0, 0, 2, 2, 2, 34, 18, 10, 6, 10, 18, 34, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [
+            0, 0, 0, 0, 0, 0, 110, 146, 146, 146, 146, 146, 146, 146, 0, 0,
+        ],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 58, 70, 66, 66, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 58, 70, 66, 66, 66, 66, 70, 58, 2, 2],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 92, 98, 66, 66, 66, 66, 98, 92, 64, 64],
+        false,
+    ),
+    ([0, 0, 0, 0, 0, 0, 58, 70, 66, 2, 2, 2, 2, 2, 0, 0], false),
+    (
+        [0, 0, 0, 0, 0, 0, 60, 66, 2, 12, 48, 64, 66, 60, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 8, 8, 8, 62, 8, 8, 8, 8, 8, 48, 0, 0], false),
+    (
+        [0, 0, 0, 0, 0, 0, 66, 66, 66, 66, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 66, 66, 66, 36, 36, 36, 24, 24, 0, 0],
+        false,
+    ),
+    (
+        [
+            0, 0, 0, 0, 0, 0, 130, 146, 146, 146, 146, 146, 146, 108, 0, 0,
+        ],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 66, 66, 36, 24, 24, 36, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 66, 66, 66, 66, 66, 100, 88, 64, 64, 60],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 126, 64, 32, 16, 8, 4, 2, 126, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 48, 8, 8, 16, 16, 8, 4, 8, 16, 16, 8, 8, 48],
+        false,
+    ),
+    (
+        [0, 0, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
+        false,
+    ),
+    (
+        [0, 0, 0, 12, 16, 16, 8, 8, 16, 32, 16, 8, 8, 16, 16, 12],
+        false,
+    ),
+    ([0, 0, 0, 140, 146, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 35790, 2131, 35794, 2131, 64462, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 47502, 19027, 52174, 19011, 47682, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 47506, 19027, 47710, 2643, 35218, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 51662, 19027, 63950, 18515, 51278, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 51666, 19031, 63962, 19027, 51666, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 40238, 9573, 42404, 9509, 40238, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 35794, 2135, 35802, 2131, 64466, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 45980, 18499, 63884, 18961, 51662, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 45982, 18499, 63902, 18947, 51678, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 47570, 1171, 39070, 8339, 40082, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 48036, 8485, 41276, 8485, 39204, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 45986, 2339, 37154, 8469, 39176, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 47182, 18515, 51278, 18499, 48066, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 51278, 18515, 51278, 18499, 46018, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 33904, 1169, 33904, 1105, 33936, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 48028, 16451, 45452, 2577, 63950, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 48028, 16451, 45452, 16913, 47566, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 62350, 2131, 45138, 16467, 48014, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 41550, 12883, 41550, 8771, 61826, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 47694, 16979, 45646, 2627, 63874, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 47580, 1155, 39052, 8337, 40078, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 52124, 18499, 63554, 18499, 52124, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 37188, 4461, 38228, 6981, 37188, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 45532, 19011, 63948, 18513, 51278, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 45534, 19011, 63966, 18499, 51294, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 61852, 2627, 45644, 16977, 47502, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 55756, 17443, 50596, 17705, 55750, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 48028, 4163, 36940, 4177, 48014, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 48028, 4163, 37250, 4611, 47580, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 40816, 1033, 33840, 1089, 33848, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 62348, 2131, 35218, 2579, 61900, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 37176, 6985, 38200, 4361, 37128, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            21845, 32768, 1, 32768, 1, 61900, 2643, 35294, 2131, 61522, 1, 32768, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    (
+        [0, 0, 0, 0, 16, 16, 0, 16, 16, 16, 16, 16, 16, 16, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 16, 16, 124, 146, 18, 18, 146, 124, 16, 16, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 112, 8, 8, 8, 62, 8, 8, 8, 124, 134, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 66, 60, 36, 66, 66, 36, 60, 66, 0, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 130, 68, 40, 16, 254, 16, 254, 16, 16, 16, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 16, 16, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 60, 66, 2, 60, 66, 66, 60, 64, 66, 60, 0, 0],
+        false,
+    ),
+    ([36, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    (
+        [
+            0, 0, 0, 0, 60, 66, 153, 165, 133, 133, 165, 153, 66, 60, 0, 0,
+        ],
+        false,
+    ),
+    (
+        [0, 0, 56, 64, 120, 68, 120, 0, 124, 0, 0, 0, 0, 0, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 72, 72, 36, 36, 18, 36, 36, 72, 72, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 64, 64, 64, 0, 0], false),
+    (
+        [
+            21845, 32768, 1, 50780, 10819, 37836, 4689, 37454, 1, 32768, 1, 33728, 1, 32768, 1,
+            43690,
+        ],
+        true,
+    ),
+    (
+        [
+            0, 0, 0, 0, 60, 66, 157, 165, 165, 157, 149, 165, 66, 60, 0, 0,
+        ],
+        false,
+    ),
+    ([0, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    ([0, 0, 0, 0, 24, 36, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    (
+        [0, 0, 0, 0, 16, 16, 16, 254, 16, 16, 16, 0, 254, 0, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 28, 34, 32, 24, 4, 2, 62, 0, 0, 0, 0, 0, 0], false),
+    (
+        [0, 0, 0, 28, 34, 32, 28, 32, 34, 28, 0, 0, 0, 0, 0, 0],
+        false,
+    ),
+    ([0, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], false),
+    (
+        [0, 0, 0, 0, 0, 0, 66, 66, 66, 66, 66, 66, 102, 154, 2, 1],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 252, 94, 94, 94, 92, 80, 80, 80, 80, 80, 80, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 0, 0, 0, 0, 0, 0], false),
+    ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 12], false),
+    ([0, 0, 0, 8, 12, 10, 8, 8, 8, 62, 0, 0, 0, 0, 0, 0], false),
+    (
+        [0, 0, 56, 68, 68, 68, 56, 0, 124, 0, 0, 0, 0, 0, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 18, 18, 36, 36, 72, 36, 36, 18, 18, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 68, 70, 36, 20, 20, 72, 104, 84, 114, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 68, 70, 36, 20, 20, 40, 88, 68, 34, 114, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 70, 72, 36, 24, 22, 72, 104, 84, 114, 66, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 8, 8, 0, 8, 8, 4, 2, 66, 66, 60, 0, 0], false),
+    (
+        [12, 48, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [48, 12, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [24, 36, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [76, 50, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [36, 36, 0, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [24, 36, 24, 0, 24, 36, 36, 66, 66, 126, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 248, 20, 18, 18, 254, 18, 18, 18, 18, 242, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 60, 66, 66, 2, 2, 2, 2, 66, 66, 60, 16, 12],
+        false,
+    ),
+    (
+        [12, 48, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 126, 0, 0],
+        false,
+    ),
+    (
+        [48, 12, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 126, 0, 0],
+        false,
+    ),
+    (
+        [24, 36, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 126, 0, 0],
+        false,
+    ),
+    (
+        [36, 36, 0, 0, 126, 2, 2, 2, 62, 2, 2, 2, 2, 126, 0, 0],
+        false,
+    ),
+    (
+        [24, 96, 0, 0, 124, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [48, 12, 0, 0, 124, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [24, 36, 0, 0, 124, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [36, 36, 0, 0, 124, 16, 16, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 30, 34, 66, 66, 79, 66, 66, 66, 34, 30, 0, 0],
+        false,
+    ),
+    (
+        [76, 50, 0, 0, 66, 70, 70, 74, 74, 82, 82, 98, 98, 66, 0, 0],
+        false,
+    ),
+    (
+        [12, 48, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [48, 12, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [24, 36, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [76, 50, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [36, 36, 0, 0, 60, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 0, 0, 0, 66, 36, 24, 36, 66, 0, 0, 0, 0], false),
+    (
+        [0, 0, 0, 64, 92, 34, 98, 82, 82, 74, 74, 70, 68, 58, 2, 0],
+        false,
+    ),
+    (
+        [12, 48, 0, 0, 66, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [48, 12, 0, 0, 66, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [24, 36, 0, 0, 66, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [36, 36, 0, 0, 66, 66, 66, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [48, 12, 0, 0, 130, 130, 68, 68, 40, 16, 16, 16, 16, 16, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 2, 2, 30, 34, 66, 66, 34, 30, 2, 2, 2, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 28, 34, 34, 18, 26, 34, 66, 66, 74, 50, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 12, 48, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 48, 12, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 24, 36, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 76, 50, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 36, 36, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 24, 36, 24, 0, 0, 60, 66, 64, 124, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 124, 146, 144, 252, 18, 18, 146, 124, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 0, 0, 0, 0, 60, 66, 2, 2, 2, 2, 66, 60, 16, 12],
+        false,
+    ),
+    (
+        [0, 0, 12, 48, 0, 0, 60, 66, 66, 126, 2, 2, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 48, 12, 0, 0, 60, 66, 66, 126, 2, 2, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 24, 36, 0, 0, 60, 66, 66, 126, 2, 2, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 36, 36, 0, 0, 60, 66, 66, 126, 2, 2, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 12, 48, 0, 0, 24, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 48, 12, 0, 0, 24, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 24, 36, 0, 0, 24, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 36, 36, 0, 0, 24, 16, 16, 16, 16, 16, 16, 124, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 76, 48, 40, 68, 64, 124, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 76, 50, 0, 0, 58, 70, 66, 66, 66, 66, 66, 66, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 12, 48, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 48, 12, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 24, 36, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 76, 50, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 36, 36, 0, 0, 60, 66, 66, 66, 66, 66, 66, 60, 0, 0],
+        false,
+    ),
+    ([0, 0, 0, 0, 0, 0, 24, 0, 0, 126, 0, 0, 24, 0, 0, 0], false),
+    (
+        [0, 0, 0, 0, 0, 64, 60, 98, 82, 82, 74, 74, 70, 60, 2, 0],
+        false,
+    ),
+    (
+        [0, 0, 12, 48, 0, 0, 66, 66, 66, 66, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 48, 12, 0, 0, 66, 66, 66, 66, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 24, 36, 0, 0, 66, 66, 66, 66, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 36, 36, 0, 0, 66, 66, 66, 66, 66, 66, 98, 92, 0, 0],
+        false,
+    ),
+    (
+        [0, 0, 48, 12, 0, 0, 66, 66, 66, 66, 66, 100, 88, 64, 64, 60],
+        false,
+    ),
+    (
+        [0, 0, 0, 2, 2, 2, 58, 70, 66, 66, 66, 66, 70, 58, 2, 2],
+        false,
+    ),
+    (
+        [0, 0, 36, 36, 0, 0, 66, 66, 66, 66, 66, 100, 88, 64, 64, 60],
+        false,
+    ),
+];
diff --git a/src/video/logger.rs b/src/video/logger.rs
index 5ebf9f7..75dcfc9 100644
--- a/src/video/logger.rs
+++ b/src/video/logger.rs
@@ -40,7 +40,18 @@ impl Writer {
         match self.escapes.next(byte) {
             DecodedPart::Byte(b'\n') => {
                 if let Some(mut fbo) = get_fbo() {
-                    self.new_line(1, &mut fbo)
+                    #[cfg(debug_assertions)]
+                    let lines = if self.cursor[1] + 1 >= Self::size(&mut fbo)[1] {
+                        // scroll more if the cursor is near the bottom
+                        //
+                        // because scrolling is slow in debug mode
+                        8
+                    } else {
+                        1
+                    };
+                    #[cfg(not(debug_assertions))]
+                    let lines = 1;
+                    self.new_line(lines, &mut fbo)
                 }
             }
             DecodedPart::Byte(b'\t') => {
-- 
GitLab