From 2cf8a98743ef9fc83df1be88f80f94baf6148bcf Mon Sep 17 00:00:00 2001 From: Adam Greenwood-Byrne Date: Tue, 14 Jul 2020 11:53:09 +0100 Subject: [PATCH] Added code for part5-framebuffer and part6-breakout, write-up todo --- part5-framebuffer/Makefile | 19 +++ part5-framebuffer/boot.S | 30 +++++ part5-framebuffer/fb.c | 172 ++++++++++++++++++++++++ part5-framebuffer/fb.h | 7 + part5-framebuffer/io.c | 166 +++++++++++++++++++++++ part5-framebuffer/io.h | 11 ++ part5-framebuffer/kernel.c | 23 ++++ part5-framebuffer/link.ld | 19 +++ part5-framebuffer/mb.c | 39 ++++++ part5-framebuffer/mb.h | 34 +++++ part5-framebuffer/terminal.h | 253 +++++++++++++++++++++++++++++++++++ part6-breakout/Makefile | 19 +++ part6-breakout/boot.S | 30 +++++ part6-breakout/breakout.c | 252 ++++++++++++++++++++++++++++++++++ part6-breakout/breakout.h | 1 + part6-breakout/fb.c | 214 +++++++++++++++++++++++++++++ part6-breakout/fb.h | 9 ++ part6-breakout/io.c | 166 +++++++++++++++++++++++ part6-breakout/io.h | 11 ++ part6-breakout/kernel.c | 10 ++ part6-breakout/link.ld | 19 +++ part6-breakout/mb.c | 39 ++++++ part6-breakout/mb.h | 34 +++++ part6-breakout/terminal.h | 253 +++++++++++++++++++++++++++++++++++ 24 files changed, 1830 insertions(+) create mode 100644 part5-framebuffer/Makefile create mode 100644 part5-framebuffer/boot.S create mode 100644 part5-framebuffer/fb.c create mode 100644 part5-framebuffer/fb.h create mode 100644 part5-framebuffer/io.c create mode 100644 part5-framebuffer/io.h create mode 100644 part5-framebuffer/kernel.c create mode 100644 part5-framebuffer/link.ld create mode 100644 part5-framebuffer/mb.c create mode 100644 part5-framebuffer/mb.h create mode 100644 part5-framebuffer/terminal.h create mode 100644 part6-breakout/Makefile create mode 100644 part6-breakout/boot.S create mode 100644 part6-breakout/breakout.c create mode 100644 part6-breakout/breakout.h create mode 100644 part6-breakout/fb.c create mode 100644 part6-breakout/fb.h create mode 100644 part6-breakout/io.c create mode 100644 part6-breakout/io.h create mode 100644 part6-breakout/kernel.c create mode 100644 part6-breakout/link.ld create mode 100644 part6-breakout/mb.c create mode 100644 part6-breakout/mb.h create mode 100644 part6-breakout/terminal.h diff --git a/part5-framebuffer/Makefile b/part5-framebuffer/Makefile new file mode 100644 index 0000000..89235e9 --- /dev/null +++ b/part5-framebuffer/Makefile @@ -0,0 +1,19 @@ +CFILES = $(wildcard *.c) +OFILES = $(CFILES:.c=.o) +GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles +GCCPATH = ../../gcc-arm-9.2-2019.12-x86_64-aarch64-none-elf/bin + +all: clean kernel8.img + +boot.o: boot.S + $(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o + +%.o: %.c + $(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@ + +kernel8.img: boot.o $(OFILES) + $(GCCPATH)/aarch64-none-elf-ld -nostdlib -nostartfiles boot.o $(OFILES) -T link.ld -o kernel8.elf + $(GCCPATH)/aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img + +clean: + /bin/rm kernel8.elf *.o *.img > /dev/null 2> /dev/null || true diff --git a/part5-framebuffer/boot.S b/part5-framebuffer/boot.S new file mode 100644 index 0000000..e09b7d4 --- /dev/null +++ b/part5-framebuffer/boot.S @@ -0,0 +1,30 @@ +.section ".text.boot" // Make sure the linker puts this at the start of the kernel image + +.global _start // Execution starts here + +_start: + // Check processor ID is zero (executing on main core), else hang + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, 2f + // We're not on the main core, so hang in an infinite wait loop +1: wfe + b 1b +2: // We're on the main core! + + // Set stack to start below our code + ldr x1, =_start + mov sp, x1 + + // Clean the BSS section + ldr x1, =__bss_start // Start address + ldr w2, =__bss_size // Size of the section +3: cbz w2, 4f // Quit loop if zero + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 3b // Loop if non-zero + + // Jump to our main() routine in C (make sure it doesn't return) +4: bl main + // In case it does return, halt the master core too + b 1b diff --git a/part5-framebuffer/fb.c b/part5-framebuffer/fb.c new file mode 100644 index 0000000..8ec796c --- /dev/null +++ b/part5-framebuffer/fb.c @@ -0,0 +1,172 @@ +#include "io.h" +#include "mb.h" +#include "terminal.h" + +unsigned int width, height, pitch, isrgb; +unsigned char *fb; + +// Refer to https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface in write-up! +void fb_init() +{ + mbox[0] = 35*4; // Length of message in bytes + mbox[1] = MBOX_REQUEST; + + mbox[2] = MBOX_TAG_SETPHYWH; // Tag identifier + mbox[3] = 8; // Value size in bytes + mbox[4] = 8; // Value size in bytes (again!) + mbox[5] = 1920; // Value(width) + mbox[6] = 1080; // Value(height) + + mbox[7] = MBOX_TAG_SETVIRTWH; + mbox[8] = 8; + mbox[9] = 8; + mbox[10] = 1920; + mbox[11] = 1080; + + mbox[12] = MBOX_TAG_SETVIRTOFF; + mbox[13] = 8; + mbox[14] = 8; + mbox[15] = 0; // Value(x) + mbox[16] = 0; // Value(y) + + mbox[17] = MBOX_TAG_SETDEPTH; + mbox[18] = 4; + mbox[19] = 4; + mbox[20] = 32; // Bits per pixel + + mbox[21] = MBOX_TAG_SETPXLORDR; + mbox[22] = 4; + mbox[23] = 4; + mbox[24] = 1; // RGB + + mbox[25] = MBOX_TAG_GETFB; + mbox[26] = 8; + mbox[27] = 8; + mbox[28] = 4096; // FrameBufferInfo.pointer + mbox[29] = 0; // FrameBufferInfo.size + + mbox[30] = MBOX_TAG_GETPITCH; + mbox[31] = 4; + mbox[32] = 4; + mbox[33] = 0; // Bytes per line + + mbox[34] = MBOX_TAG_LAST; + + // Check call is successful and we have a pointer with depth 32 + if (mbox_call(MBOX_CH_PROP) && mbox[20] == 32 && mbox[28] != 0) { + mbox[28] &= 0x3FFFFFFF; // Convert GPU address to ARM address + width = mbox[10]; // Actual physical width + height = mbox[11]; // Actual physical height + pitch = mbox[33]; // Number of bytes per line + isrgb = mbox[24]; // Pixel order + fb = (unsigned char *)((long)mbox[28]); + } +} + +void drawPixel(int x, int y, unsigned char attr) +{ + int offs = (y * pitch) + (x * 4); + *((unsigned int*)(fb + offs)) = vgapal[attr & 0x0f]; +} + +void drawChar(unsigned char ch, int x, int y, unsigned char attr) +{ + unsigned char *glyph = (unsigned char *)&font + (ch < FONT_NUMGLYPHS ? ch : 0) * FONT_BPG; + + for (int i=1;i<=FONT_HEIGHT;i++) { + for (int j=0;j> 4; + + drawPixel(x+j, y+i, col); + } + glyph += FONT_BPL; + } +} + +void drawString(int x, int y, char *s, unsigned char attr) +{ + while(*s) { + if (*s == '\r') { + x = 0; + } else if(*s == '\n') { + x = 0; y += FONT_HEIGHT; + } else { + drawChar(*s, x, y, attr); + x += FONT_WIDTH; + } + s++; + } +} + +void drawRect(int x1, int y1, int x2, int y2, unsigned char attr, int fill) +{ + int y=y1; + + while (y <= y2) { + int x=x1; + while (x <= x2) { + if (fill || (x == x1 || x == x2) || (y == y1 || y == y2)) drawPixel(x, y, attr); + x++; + } + y++; + } +} + +void drawLine(int x1, int y1, int x2, int y2, unsigned char attr) +{ + int dx, dy, p, x, y; + + dx = x2-x1; + dy = y2-y1; + x = x1; + y = y1; + p = 2*dy-dx; + + while (x= 0) { + drawPixel(x,y,attr); + y++; + p = p+2*dy-2*dx; + } else { + drawPixel(x,y,attr); + p = p+2*dy; + } + x++; + } +} + +void drawCircle(int x0, int y0, int radius, unsigned char attr, int fill) +{ + int x = radius; + int y = 0; + int err = 0; + + while (x >= y) { + if (fill) { + drawLine(x0 - y, y0 + x, x0 + y, y0 + x, attr); + drawLine(x0 - x, y0 + y, x0 + x, y0 + y, attr); + drawLine(x0 - x, y0 - y, x0 + x, y0 - y, attr); + drawLine(x0 - y, y0 - x, x0 + y, y0 - x, attr); + } else { + drawPixel(x0 - y, y0 + x, attr); + drawPixel(x0 + y, y0 + x, attr); + drawPixel(x0 - x, y0 + y, attr); + drawPixel(x0 + x, y0 + y, attr); + drawPixel(x0 - x, y0 - y, attr); + drawPixel(x0 + x, y0 - y, attr); + drawPixel(x0 - y, y0 - x, attr); + drawPixel(x0 + y, y0 - x, attr); + } + + if (err <= 0) { + y += 1; + err += 2*y + 1; + } + + if (err > 0) { + x -= 1; + err -= 2*x + 1; + } + } +} diff --git a/part5-framebuffer/fb.h b/part5-framebuffer/fb.h new file mode 100644 index 0000000..06f71f8 --- /dev/null +++ b/part5-framebuffer/fb.h @@ -0,0 +1,7 @@ +void fb_init(); +void drawPixel(int x, int y, unsigned char attr); +void drawChar(unsigned char ch, int x, int y, unsigned char attr); +void drawString(int x, int y, char *s, unsigned char attr); +void drawRect(int x1, int y1, int x2, int y2, unsigned char attr, int fill); +void drawCircle(int x0, int y0, int radius, unsigned char attr, int fill); +void drawLine(int x1, int y1, int x2, int y2, unsigned char attr); diff --git a/part5-framebuffer/io.c b/part5-framebuffer/io.c new file mode 100644 index 0000000..e8af328 --- /dev/null +++ b/part5-framebuffer/io.c @@ -0,0 +1,166 @@ +#include "io.h" + +// GPIO + +enum { + GPFSEL0 = PERIPHERAL_BASE + 0x200000, + GPSET0 = PERIPHERAL_BASE + 0x20001C, + GPCLR0 = PERIPHERAL_BASE + 0x200028, + GPPUPPDN0 = PERIPHERAL_BASE + 0x2000E4 +}; + +enum { + GPIO_MAX_PIN = 53, + GPIO_FUNCTION_OUT = 1, + GPIO_FUNCTION_ALT5 = 2, + GPIO_FUNCTION_ALT3 = 7 +}; + +enum { + Pull_None = 0, + Pull_Down = 1, // Are down and up the right way around? + Pull_Up = 2 +}; + +void mmio_write(long reg, unsigned int val) { *(volatile unsigned int *)reg = val; } +unsigned int mmio_read(long reg) { return *(volatile unsigned int *)reg; } + +unsigned int gpio_call(unsigned int pin_number, unsigned int value, unsigned int base, unsigned int field_size, unsigned int field_max) { + unsigned int field_mask = (1 << field_size) - 1; + + if (pin_number > field_max) return 0; + if (value > field_mask) return 0; + + unsigned int num_fields = 32 / field_size; + unsigned int reg = base + ((pin_number / num_fields) * 4); + unsigned int shift = (pin_number % num_fields) * field_size; + + unsigned int curval = mmio_read(reg); + curval &= ~(field_mask << shift); + curval |= value << shift; + mmio_write(reg, curval); + + return 1; +} + +unsigned int gpio_set (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPSET0, 1, GPIO_MAX_PIN); } +unsigned int gpio_clear (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPCLR0, 1, GPIO_MAX_PIN); } +unsigned int gpio_pull (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPPUPPDN0, 2, GPIO_MAX_PIN); } +unsigned int gpio_function(unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPFSEL0, 3, GPIO_MAX_PIN); } + +void gpio_useAsAlt3(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_ALT3); +} + +void gpio_useAsAlt5(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_ALT5); +} + +void gpio_initOutputPinWithPullNone(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_OUT); +} + +void gpio_setPinOutputBool(unsigned int pin_number, unsigned int onOrOff) { + if (onOrOff) { + gpio_set(pin_number, 1); + } else { + gpio_clear(pin_number, 1); + } +} + +// UART + +enum { + AUX_BASE = PERIPHERAL_BASE + 0x215000, + AUX_IRQ = AUX_BASE, + AUX_ENABLES = AUX_BASE + 4, + AUX_MU_IO_REG = AUX_BASE + 64, + AUX_MU_IER_REG = AUX_BASE + 68, + AUX_MU_IIR_REG = AUX_BASE + 72, + AUX_MU_LCR_REG = AUX_BASE + 76, + AUX_MU_MCR_REG = AUX_BASE + 80, + AUX_MU_LSR_REG = AUX_BASE + 84, + AUX_MU_MSR_REG = AUX_BASE + 88, + AUX_MU_SCRATCH = AUX_BASE + 92, + AUX_MU_CNTL_REG = AUX_BASE + 96, + AUX_MU_STAT_REG = AUX_BASE + 100, + AUX_MU_BAUD_REG = AUX_BASE + 104, + AUX_UART_CLOCK = 500000000, + UART_MAX_QUEUE = 16 * 1024 +}; + +#define AUX_MU_BAUD(baud) ((AUX_UART_CLOCK/(baud*8))-1) + +unsigned char uart_output_queue[UART_MAX_QUEUE]; +unsigned int uart_output_queue_write = 0; +unsigned int uart_output_queue_read = 0; + +void uart_init() { + mmio_write(AUX_ENABLES, 1); //enable UART1 + mmio_write(AUX_MU_IER_REG, 0); + mmio_write(AUX_MU_CNTL_REG, 0); + mmio_write(AUX_MU_LCR_REG, 3); //8 bits + mmio_write(AUX_MU_MCR_REG, 0); + mmio_write(AUX_MU_IER_REG, 0); + mmio_write(AUX_MU_IIR_REG, 0xC6); //disable interrupts + mmio_write(AUX_MU_BAUD_REG, AUX_MU_BAUD(115200)); + gpio_useAsAlt5(14); + gpio_useAsAlt5(15); + mmio_write(AUX_MU_CNTL_REG, 3); //enable RX/TX +} + +unsigned int uart_isOutputQueueEmpty() { + return uart_output_queue_read == uart_output_queue_write; +} + +unsigned int uart_isReadByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x01; } +unsigned int uart_isWriteByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x20; } + +unsigned char uart_readByte() { + while (!uart_isReadByteReady()); + return (unsigned char)mmio_read(AUX_MU_IO_REG); +} + +void uart_writeByteBlockingActual(unsigned char ch) { + while (!uart_isWriteByteReady()); + mmio_write(AUX_MU_IO_REG, (unsigned int)ch); +} + +void uart_loadOutputFifo() { + while (!uart_isOutputQueueEmpty() && uart_isWriteByteReady()) { + uart_writeByteBlockingActual(uart_output_queue[uart_output_queue_read]); + uart_output_queue_read = (uart_output_queue_read + 1) & (UART_MAX_QUEUE - 1); // Don't overrun + } +} + +void uart_writeByteBlocking(unsigned char ch) { + unsigned int next = (uart_output_queue_write + 1) & (UART_MAX_QUEUE - 1); // Don't overrun + + while (next == uart_output_queue_read) uart_loadOutputFifo(); + + uart_output_queue[uart_output_queue_write] = ch; + uart_output_queue_write = next; +} + +void uart_writeText(char *buffer) { + while (*buffer) { + if (*buffer == '\n') uart_writeByteBlocking('\r'); + uart_writeByteBlocking(*buffer++); + } +} + +void uart_drainOutputQueue() { + while (!uart_isOutputQueueEmpty()) uart_loadOutputFifo(); +} + +void uart_update() { + uart_loadOutputFifo(); + + if (uart_isReadByteReady()) { + unsigned char ch = uart_readByte(); + if (ch == '\r') uart_writeText("\n"); else uart_writeByteBlocking(ch); + } +} diff --git a/part5-framebuffer/io.h b/part5-framebuffer/io.h new file mode 100644 index 0000000..2a412e6 --- /dev/null +++ b/part5-framebuffer/io.h @@ -0,0 +1,11 @@ +#define PERIPHERAL_BASE 0xFE000000 + +void uart_init(); +void uart_writeText(char *buffer); +void uart_loadOutputFifo(); +unsigned char uart_readByte(); +unsigned int uart_isReadByteReady(); +void uart_writeByteBlocking(unsigned char ch); +void uart_update(); +void mmio_write(long reg, unsigned int val); +unsigned int mmio_read(long reg); diff --git a/part5-framebuffer/kernel.c b/part5-framebuffer/kernel.c new file mode 100644 index 0000000..0623982 --- /dev/null +++ b/part5-framebuffer/kernel.c @@ -0,0 +1,23 @@ +#include "io.h" +#include "fb.h" + +void main() +{ + uart_init(); + fb_init(); + + drawRect(150,150,400,400,0x03,0); + drawRect(300,300,350,350,0x02,1); + + drawCircle(960,540,250,0x0e,0); + drawCircle(960,540,50,0x04,1); + + drawPixel(250,250,0x0e); + + drawChar('O',500,500,0x05); + drawString(100,100,"Hello world!",0x0f); + + drawLine(100,500,350,700,0x0c); + + while (1); +} diff --git a/part5-framebuffer/link.ld b/part5-framebuffer/link.ld new file mode 100644 index 0000000..dfbf022 --- /dev/null +++ b/part5-framebuffer/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x80000; /* Kernel load address for AArch64 */ + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; diff --git a/part5-framebuffer/mb.c b/part5-framebuffer/mb.c new file mode 100644 index 0000000..f9fb7a0 --- /dev/null +++ b/part5-framebuffer/mb.c @@ -0,0 +1,39 @@ +#include "io.h" + +// The buffer must be 16-byte aligned as only the upper 28 bits of the address can be passed via the mailbox +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +enum { + VIDEOCORE_MBOX = (PERIPHERAL_BASE + 0x0000B880), + MBOX_READ = (VIDEOCORE_MBOX + 0x0), + MBOX_POLL = (VIDEOCORE_MBOX + 0x10), + MBOX_SENDER = (VIDEOCORE_MBOX + 0x14), + MBOX_STATUS = (VIDEOCORE_MBOX + 0x18), + MBOX_CONFIG = (VIDEOCORE_MBOX + 0x1C), + MBOX_WRITE = (VIDEOCORE_MBOX + 0x20), + MBOX_RESPONSE = 0x80000000, + MBOX_FULL = 0x80000000, + MBOX_EMPTY = 0x40000000 +}; + +unsigned int mbox_call(unsigned char ch) +{ + // 28-bit address (MSB) and 4-bit value (LSB) + unsigned int r = ((unsigned int)((long) &mbox) &~ 0xF) | (ch & 0xF); + + // Wait until we can write + while (mmio_read(MBOX_STATUS) & MBOX_FULL); + + /* write the address of our message to the mailbox with channel identifier */ + mmio_write(MBOX_WRITE, r); + + while (1) { + // Is there a reply? + while (mmio_read(MBOX_STATUS) & MBOX_EMPTY); + + // Is it a reply to our message? + if (r == mmio_read(MBOX_READ)) return mbox[1]==MBOX_RESPONSE; // Is it successful? + + } + return 0; +} diff --git a/part5-framebuffer/mb.h b/part5-framebuffer/mb.h new file mode 100644 index 0000000..0e1861b --- /dev/null +++ b/part5-framebuffer/mb.h @@ -0,0 +1,34 @@ +extern volatile unsigned int mbox[36]; + +enum { + MBOX_REQUEST = 0 +}; + +enum { + MBOX_CH_POWER = 0, + MBOX_CH_FB = 1, + MBOX_CH_VUART = 2, + MBOX_CH_VCHIQ = 3, + MBOX_CH_LEDS = 4, + MBOX_CH_BTNS = 5, + MBOX_CH_TOUCH = 6, + MBOX_CH_COUNT = 7, + MBOX_CH_PROP = 8 // Request from ARM for response by VideoCore +}; + +enum { + MBOX_TAG_SETPOWER = 0x28001, + MBOX_TAG_SETCLKRATE = 0x38002, + + MBOX_TAG_SETPHYWH = 0x48003, + MBOX_TAG_SETVIRTWH = 0x48004, + MBOX_TAG_SETVIRTOFF = 0x48009, + MBOX_TAG_SETDEPTH = 0x48005, + MBOX_TAG_SETPXLORDR = 0x48006, + MBOX_TAG_GETFB = 0x40001, + MBOX_TAG_GETPITCH = 0x40008, + + MBOX_TAG_LAST = 0 +}; + +unsigned int mbox_call(unsigned char ch); diff --git a/part5-framebuffer/terminal.h b/part5-framebuffer/terminal.h new file mode 100644 index 0000000..ef05b85 --- /dev/null +++ b/part5-framebuffer/terminal.h @@ -0,0 +1,253 @@ +unsigned int vgapal[] = { + 0x000000, + 0x0000AA, + 0x00AA00, + 0x00AAAA, + 0xAA0000, + 0xAA00AA, + 0xAA5500, + 0xAAAAAA, + 0x555555, + 0x5555FF, + 0x55FF55, + 0x55FFFF, + 0xFF5555, + 0xFF55FF, + 0xFFFF55, + 0xFFFFFF +}; + +enum { + FONT_WIDTH = 8, + FONT_HEIGHT = 8, + FONT_BPG = 8, // Bytes per glyph + FONT_BPL = 1, // Bytes per line + FONT_NUMGLYPHS = 224 +}; + +unsigned char font[FONT_NUMGLYPHS][FONT_BPG] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) + { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!) + { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (") + { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#) + { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($) + { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%) + { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&) + { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (') + { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (() + { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ()) + { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*) + { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,) + { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) + { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) + { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) + { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) + { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) + { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) + { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) + { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) + { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) + { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) + { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) + { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;) + { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<) + { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=) + { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>) + { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?) + { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@) + { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) + { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) + { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) + { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) + { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) + { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) + { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) + { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) + { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) + { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) + { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) + { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) + { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) + { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) + { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) + { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) + { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) + { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) + { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) + { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) + { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) + { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) + { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([) + { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\) + { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (]) + { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_) + { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`) + { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) + { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) + { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) + { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) + { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) + { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) + { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) + { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) + { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) + { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) + { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) + { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) + { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) + { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) + { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) + { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) + { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) + { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) + { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) + { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) + { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) + { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({) + { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|) + { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (}) + { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007F + { 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x18, 0x30, 0x1E}, // U+00C7 (C cedille) + { 0x00, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FC (u umlaut) + { 0x38, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E9 (e aigu) + { 0x7E, 0xC3, 0x3C, 0x60, 0x7C, 0x66, 0xFC, 0x00}, // U+00E2 (a circumflex) + { 0x33, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E4 (a umlaut) + { 0x07, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E0 (a grave) + { 0x0C, 0x0C, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E5 (a ring) + { 0x00, 0x00, 0x1E, 0x03, 0x03, 0x1E, 0x30, 0x1C}, // U+00E7 (c cedille) + { 0x7E, 0xC3, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00}, // U+00EA (e circumflex) + { 0x33, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00EB (e umlaut) + { 0x07, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E8 (e grave) + { 0x33, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EF (i umlaut) + { 0x3E, 0x63, 0x1C, 0x18, 0x18, 0x18, 0x3C, 0x00}, // U+00EE (i circumflex) + { 0x07, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EC (i grave) + { 0x63, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x63, 0x00}, // U+00C4 (A umlaut) + { 0x0C, 0x0C, 0x00, 0x1E, 0x33, 0x3F, 0x33, 0x00}, // U+00C5 (A ring) + { 0x07, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00}, // U+00C8 (E grave) + { 0x00, 0x00, 0xFE, 0x30, 0xFE, 0x33, 0xFE, 0x00}, // U+00E6 (ae) + { 0x7C, 0x36, 0x33, 0x7F, 0x33, 0x33, 0x73, 0x00}, // U+00C6 (AE) + { 0x1E, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F4 (o circumflex) + { 0x00, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F6 (o umlaut) + { 0x00, 0x07, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F2 (o grave) + { 0x1E, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FB (u circumflex) + { 0x00, 0x07, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00F9 (u grave) + { 0x00, 0x33, 0x00, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+00FF (y umlaut) + { 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00}, // U+00D6 (O umlaut) + { 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+00DC (U umlaut) + { 0x18, 0x18, 0x7E, 0x03, 0x03, 0x7E, 0x18, 0x18}, // U+00A2 (dollarcents) + { 0x1C, 0x36, 0x26, 0x0F, 0x06, 0x67, 0x3F, 0x00}, // U+00A3 (pound sterling) + { 0x33, 0x33, 0x1E, 0x3F, 0x0C, 0x3F, 0x0C, 0x0C}, // U+00A5 (yen) + { 0x7C, 0xC6, 0x1C, 0x36, 0x36, 0x1C, 0x33, 0x1E}, // U+00A7 (paragraph) + { 0x70, 0xD8, 0x18, 0x3C, 0x18, 0x18, 0x1B, 0x0E}, // U+0192 (dutch florijn) + { 0x38, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E1 (a aigu) + { 0x1C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00ED (i augu) + { 0x00, 0x38, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F3 (o aigu) + { 0x00, 0x38, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FA (u aigu) + { 0x00, 0x1F, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x00}, // U+00F1 (n ~) + { 0x3F, 0x00, 0x33, 0x37, 0x3F, 0x3B, 0x33, 0x00}, // U+00D1 (N ~) + { 0x3C, 0x36, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00}, // U+00AA (superscript a) + { 0x1C, 0x36, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00}, // U+00BA (superscript 0) + { 0x0C, 0x00, 0x0C, 0x06, 0x03, 0x33, 0x1E, 0x00}, // U+00BF (inverted ?) + { 0x00, 0x00, 0x00, 0x3F, 0x03, 0x03, 0x00, 0x00}, // U+2310 (gun pointing right) + { 0x00, 0x00, 0x00, 0x3F, 0x30, 0x30, 0x00, 0x00}, // U+00AC (gun pointing left) + { 0xC3, 0x63, 0x33, 0x7B, 0xCC, 0x66, 0x33, 0xF0}, // U+00BD (1/2) + { 0xC3, 0x63, 0x33, 0xBD, 0xEC, 0xF6, 0xF3, 0x03}, // U+00BC (1/4) + { 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00}, // U+00A1 (inverted !) + { 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00}, // U+00AB (<<) + { 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00}, // U+00BB (>>) + { 0x55, 0x00, 0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00}, // U+2591 (25% solid) + { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA}, // U+2592 (50% solid) + { 0xFF, 0xAA, 0xFF, 0x55, 0xFF, 0xAA, 0xFF, 0x55}, // U+2593 (75% solid) + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, // U+2502 (thin vertical) + { 0x08, 0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x08}, // U+2524 (down L, left L, up L) + { 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2561 (up L, down L, left D) + { 0x14, 0x14, 0x14, 0x14, 0x17, 0x14, 0x14, 0x14}, // U+2562 (up D, down D, left L) + { 0x00, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x14, 0x14}, // U+2556 (down D, left L) + { 0x00, 0x00, 0x00, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2555 (down L, left D) + { 0x14, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14}, // U+2563 (up D, down D, left D) + { 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, // U+2551 (double vertical) + { 0x00, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x14, 0x14}, // U+2557 (down D, left D) + { 0x14, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x00, 0x00}, // U+255D (up D, left D) + { 0x14, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x00, 0x00}, // U+255C (up D, left L) + { 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x00, 0x00}, // U+255B (up L, left D) + { 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08}, // U+2510 (down L, left L) + { 0x08, 0x08, 0x08, 0x08, 0xf8, 0x00, 0x00, 0x00}, // U+2514 (up L, right L) + { 0x08, 0x08, 0x08, 0x08, 0xff, 0x00, 0x00, 0x00}, // U+2534 (up L, right L, left L) + { 0x00, 0x00, 0x00, 0x00, 0xff, 0x08, 0x08, 0x08}, // U+252C (down L, right L, left L) + { 0x08, 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, 0x08}, // U+251C (down L, right L, up L) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00}, // U+2500 (thin horizontal) + { 0x08, 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08}, // U+253C (up L, right L, left L, down L) + { 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+255E (up L, down L, right D) + { 0x14, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x14, 0x14}, // U+255F (up D, down D, right L) + { 0x14, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x00, 0x00}, // U+255A (up D, right D) + { 0x00, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14}, // U+2554 (down D, right D) + { 0x14, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00}, // U+2569 (left D, right D, up D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14}, // U+2566 (left D, right D, down D) + { 0x14, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x14, 0x14}, // U+2560 (up D, down D, right D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2550 (double horizontal) + { 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14}, // U+256C (left D, right D, down D, up D) + { 0x08, 0x08, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2567 (left D, right D, up L) + { 0x14, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x00, 0x00}, // U+2568 (left L, right L, up D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x08, 0x08}, // U+2564 (left D, right D, down L) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x14, 0x14}, // U+2565 (left L, right L, down D) + { 0x14, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x00, 0x00}, // U+2559 (up D, right L) + { 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x00, 0x00}, // U+2558 (up L, right D) + { 0x00, 0x00, 0x00, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+2552 (down L, right D) + { 0x00, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x14, 0x14}, // U+2553 (down D, right L) + { 0x14, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x14, 0x14}, // U+256B (left L, right L, down D, up D) + { 0x08, 0x08, 0x08, 0xFF, 0x08, 0xFF, 0x08, 0x08}, // U+256A (left D, right D, down L, up L) + { 0x08, 0x08, 0x08, 0x08, 0x0f, 0x00, 0x00, 0x00}, // U+2518 (up L, left L) + { 0x00, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08}, // U+250C (down L, right L) + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2588 (solid) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2584 (bottom half) + { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, // U+258C (left half) + { 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, // U+2590 (right half) + { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, // U+2580 (top half) +}; diff --git a/part6-breakout/Makefile b/part6-breakout/Makefile new file mode 100644 index 0000000..89235e9 --- /dev/null +++ b/part6-breakout/Makefile @@ -0,0 +1,19 @@ +CFILES = $(wildcard *.c) +OFILES = $(CFILES:.c=.o) +GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles +GCCPATH = ../../gcc-arm-9.2-2019.12-x86_64-aarch64-none-elf/bin + +all: clean kernel8.img + +boot.o: boot.S + $(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o + +%.o: %.c + $(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@ + +kernel8.img: boot.o $(OFILES) + $(GCCPATH)/aarch64-none-elf-ld -nostdlib -nostartfiles boot.o $(OFILES) -T link.ld -o kernel8.elf + $(GCCPATH)/aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img + +clean: + /bin/rm kernel8.elf *.o *.img > /dev/null 2> /dev/null || true diff --git a/part6-breakout/boot.S b/part6-breakout/boot.S new file mode 100644 index 0000000..e09b7d4 --- /dev/null +++ b/part6-breakout/boot.S @@ -0,0 +1,30 @@ +.section ".text.boot" // Make sure the linker puts this at the start of the kernel image + +.global _start // Execution starts here + +_start: + // Check processor ID is zero (executing on main core), else hang + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, 2f + // We're not on the main core, so hang in an infinite wait loop +1: wfe + b 1b +2: // We're on the main core! + + // Set stack to start below our code + ldr x1, =_start + mov sp, x1 + + // Clean the BSS section + ldr x1, =__bss_start // Start address + ldr w2, =__bss_size // Size of the section +3: cbz w2, 4f // Quit loop if zero + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 3b // Loop if non-zero + + // Jump to our main() routine in C (make sure it doesn't return) +4: bl main + // In case it does return, halt the master core too + b 1b diff --git a/part6-breakout/breakout.c b/part6-breakout/breakout.c new file mode 100644 index 0000000..36abe25 --- /dev/null +++ b/part6-breakout/breakout.c @@ -0,0 +1,252 @@ +#include "fb.h" +#include "io.h" + +// The screen +#define WIDTH 1920 +#define HEIGHT 1080 +#define MARGIN 75 +#define VIRTWIDTH (WIDTH-(2*MARGIN)) +#define FONT_BPG 8 + +// For the bricks +#define ROWS 5 +#define COLS 10 +unsigned int bricks = ROWS * COLS; + +// Gameplay +#define NUM_LIVES 3 + +// OBJECT TRACKING + +struct Object +{ + unsigned int type; + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + unsigned char alive; +}; + +enum { + OBJ_NONE = 0, + OBJ_BRICK = 1, + OBJ_PADDLE = 2, + OBJ_BALL = 3 +}; + +unsigned int numobjs = 0; +struct Object objects[(ROWS * COLS) + (2 * NUM_LIVES)]; +struct Object *ball; +struct Object *paddle; + +void removeObject(struct Object *object) +{ + drawRect(object->x, object->y, object->x + object->width, object->y + object->height, 0, 1); + object->alive = 0; +} + +void moveObject(struct Object *object, int xoff, int yoff) +{ + moveRect(object->x, object->y, object->width, object->height, xoff, yoff, 0x00); + object->x = object->x + xoff; + object->y = object->y + yoff; +} + +struct Object *objectAt(unsigned int x, unsigned int y) +{ + for (int i=0; i= objects[i].x && x <= objects[i].x + objects[i].width) { + if (y >= objects[i].y && y <= objects[i].y + objects[i].height) { + if (&objects[i] != ball && objects[i].alive == 1) { + return &objects[i]; + } + } + } + } + return 0; +} + +struct Object *detectCollision(int xoff, int yoff) +{ + struct Object *collision; + + unsigned int x = ball->x + xoff; + unsigned int y = ball->y + yoff; + + collision = objectAt(x,y); + if (collision) return collision; + + collision = objectAt(x + ball->width, y); + if (collision) return collision; + + collision = objectAt(x, y + ball->height); + if (collision) return collision; + + collision = objectAt(x + ball->width, y + ball->height); + if (collision) return collision; + + return 0; +} + +// KEY HANDLER + +unsigned char getUart() +{ + unsigned char ch = 0; + + if (uart_isReadByteReady()) ch = uart_readByte(); + return ch; +} + +// OBJECT INITIALISERS + +void initBricks() +{ + int brickwidth = 32; + int brickheight = 8; + int brickspacer = 20; + int brickcols[5] = { 0x01, 0x02, 0x0E, 0x04, 0x06 }; + + int ybrick = MARGIN + brickheight; + + for (int i=0; i 0 && bricks > 0) { + // Get any waiting input and flush the buffer + if ( ( ch = getUart() ) ) { + if (ch == 'l') if (paddle->x + paddle->width + (paddle->width / 2) <= WIDTH-MARGIN) moveObject(paddle, paddle->width / 2, 0); + if (ch == 'h') if (paddle->x >= MARGIN+(paddle->width / 2)) moveObject(paddle, -(paddle->width / 2), 0); + } + uart_loadOutputFifo(); + + // Are we going to hit anything? + foundObject = detectCollision(velocity_x, velocity_y); + + if (foundObject) { + if (foundObject == paddle) { + velocity_y = -velocity_y; + // Are we going to hit the side of the paddle + if (ball->x + ball->width + velocity_x == paddle->x || ball->x + velocity_x == paddle->x + paddle->width) velocity_x = -velocity_x; + } else if (foundObject->type == OBJ_BRICK) { + removeObject(foundObject); + velocity_y = -velocity_y; + bricks--; + points++; + drawScoreboard(points, lives); + } + } + + wait_msec(4000); // Wait for a tenth of a second + moveObject(ball, velocity_x, velocity_y); + + // Check we're in the game arena still + if (ball->x + ball->width >= WIDTH-MARGIN) { + velocity_x = -velocity_x; + } else if (ball->x <= MARGIN) { + velocity_x = -velocity_x; + } else if (ball->y + ball->height >= HEIGHT-MARGIN) { + lives--; + + removeObject(ball); + removeObject(paddle); + + initBall(); + initPaddle(); + drawScoreboard(points, lives); + } else if (ball->y <= MARGIN) { + velocity_y = -velocity_y; + } + } + + int zoom = WIDTH/192; + int strwidth = 10 * FONT_BPG * zoom; + int strheight = FONT_BPG * zoom; + + if (bricks == 0) drawString((WIDTH/2)-(strwidth/2), (HEIGHT/2)-(strheight/2), "Well done!", 0x02, zoom); + else drawString((WIDTH/2)-(strwidth/2), (HEIGHT/2)-(strheight/2), "Game over!", 0x04, zoom); +} diff --git a/part6-breakout/breakout.h b/part6-breakout/breakout.h new file mode 100644 index 0000000..3fb5023 --- /dev/null +++ b/part6-breakout/breakout.h @@ -0,0 +1 @@ +void game(); diff --git a/part6-breakout/fb.c b/part6-breakout/fb.c new file mode 100644 index 0000000..ba6ddb1 --- /dev/null +++ b/part6-breakout/fb.c @@ -0,0 +1,214 @@ +#include "io.h" +#include "mb.h" +#include "terminal.h" + +unsigned int width, height, pitch, isrgb; +unsigned char *fb; + +// Refer to https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface in write-up! +void fb_init() +{ + mbox[0] = 35*4; // Length of message in bytes + mbox[1] = MBOX_REQUEST; + + mbox[2] = MBOX_TAG_SETPHYWH; // Tag identifier + mbox[3] = 8; // Value size in bytes + mbox[4] = 8; // Value size in bytes (again!) + mbox[5] = 1920; // Value(width) + mbox[6] = 1080; // Value(height) + + mbox[7] = MBOX_TAG_SETVIRTWH; + mbox[8] = 8; + mbox[9] = 8; + mbox[10] = 1920; + mbox[11] = 1080; + + mbox[12] = MBOX_TAG_SETVIRTOFF; + mbox[13] = 8; + mbox[14] = 8; + mbox[15] = 0; // Value(x) + mbox[16] = 0; // Value(y) + + mbox[17] = MBOX_TAG_SETDEPTH; + mbox[18] = 4; + mbox[19] = 4; + mbox[20] = 32; // Bits per pixel + + mbox[21] = MBOX_TAG_SETPXLORDR; + mbox[22] = 4; + mbox[23] = 4; + mbox[24] = 1; // RGB + + mbox[25] = MBOX_TAG_GETFB; + mbox[26] = 8; + mbox[27] = 8; + mbox[28] = 4096; // FrameBufferInfo.pointer + mbox[29] = 0; // FrameBufferInfo.size + + mbox[30] = MBOX_TAG_GETPITCH; + mbox[31] = 4; + mbox[32] = 4; + mbox[33] = 0; // Bytes per line + + mbox[34] = MBOX_TAG_LAST; + + // Check call is successful and we have a pointer with depth 32 + if (mbox_call(MBOX_CH_PROP) && mbox[20] == 32 && mbox[28] != 0) { + mbox[28] &= 0x3FFFFFFF; // Convert GPU address to ARM address + width = mbox[10]; // Actual physical width + height = mbox[11]; // Actual physical height + pitch = mbox[33]; // Number of bytes per line + isrgb = mbox[24]; // Pixel order + fb = (unsigned char *)((long)mbox[28]); + } +} + +void drawPixel(int x, int y, unsigned char attr) +{ + int offs = (y * pitch) + (x * 4); + *((unsigned int*)(fb + offs)) = vgapal[attr & 0x0f]; +} + +void drawChar(unsigned char ch, int x, int y, unsigned char attr, int zoom) +{ + unsigned char *glyph = (unsigned char *)&font + (ch < FONT_NUMGLYPHS ? ch : 0) * FONT_BPG; + + for (int i=1;i<=(FONT_HEIGHT*zoom);i++) { + for (int j=0;j<(FONT_WIDTH*zoom);j++) { + unsigned char mask = 1 << (j/zoom); + unsigned char col = (*glyph & mask) ? attr & 0x0f : (attr & 0xf0) >> 4; + + drawPixel(x+j, y+i, col); + } + glyph += (i%zoom) ? 0 : FONT_BPL; + } +} + +void drawString(int x, int y, char *s, unsigned char attr, int zoom) +{ + while(*s) { + if (*s == '\r') { + x = 0; + } else if(*s == '\n') { + x = 0; y += FONT_HEIGHT; + } else { + drawChar(*s, x, y, attr, zoom); + x += (FONT_WIDTH*zoom); + } + s++; + } +} + +void drawRect(int x1, int y1, int x2, int y2, unsigned char attr, int fill) +{ + int y=y1; + + while (y <= y2) { + int x=x1; + while (x <= x2) { + if (fill || (x == x1 || x == x2) || (y == y1 || y == y2)) drawPixel(x, y, attr); + x++; + } + y++; + } +} + +void moveRect(int oldx, int oldy, int width, int height, int shiftx, int shifty, unsigned char attr) +{ + unsigned int newx = oldx + shiftx, newy = oldy + shifty; + unsigned int xcount = 0, ycount = 0; + unsigned int bitmap[width][height]; // This is very unsafe if it's too big for the stack... + unsigned int offs; + + // Save the bitmap + while (xcount < width) { + while (ycount < height) { + offs = ((oldy + ycount) * pitch) + ((oldx + xcount) * 4); + + bitmap[xcount][ycount] = *((unsigned int*)(fb + offs)); + ycount++; + } + ycount=0; + xcount++; + } + // Wipe it out with background colour + drawRect(oldx, oldy, oldx + width, oldy + width, attr, 1); + // Draw it again + for (int i=newx;i= 0) { + drawPixel(x,y,attr); + y++; + p = p+2*dy-2*dx; + } else { + drawPixel(x,y,attr); + p = p+2*dy; + } + x++; + } +} + +void drawCircle(int x0, int y0, int radius, unsigned char attr, int fill) +{ + int x = radius; + int y = 0; + int err = 0; + + while (x >= y) { + if (fill) { + drawLine(x0 - y, y0 + x, x0 + y, y0 + x, attr); + drawLine(x0 - x, y0 + y, x0 + x, y0 + y, attr); + drawLine(x0 - x, y0 - y, x0 + x, y0 - y, attr); + drawLine(x0 - y, y0 - x, x0 + y, y0 - x, attr); + } else { + drawPixel(x0 - y, y0 + x, attr); + drawPixel(x0 + y, y0 + x, attr); + drawPixel(x0 - x, y0 + y, attr); + drawPixel(x0 + x, y0 + y, attr); + drawPixel(x0 - x, y0 - y, attr); + drawPixel(x0 + x, y0 - y, attr); + drawPixel(x0 - y, y0 - x, attr); + drawPixel(x0 + y, y0 - x, attr); + } + + if (err <= 0) { + y += 1; + err += 2*y + 1; + } + + if (err > 0) { + x -= 1; + err -= 2*x + 1; + } + } +} + +void wait_msec(unsigned int n) +{ + register unsigned long f, t, r; + + // Get the current counter frequency + asm volatile ("mrs %0, cntfrq_el0" : "=r"(f)); + // Read the current counter + asm volatile ("mrs %0, cntpct_el0" : "=r"(t)); + // Calculate expire value for counter + t+=((f/1000)*n)/1000; + do{asm volatile ("mrs %0, cntpct_el0" : "=r"(r));}while(r field_max) return 0; + if (value > field_mask) return 0; + + unsigned int num_fields = 32 / field_size; + unsigned int reg = base + ((pin_number / num_fields) * 4); + unsigned int shift = (pin_number % num_fields) * field_size; + + unsigned int curval = mmio_read(reg); + curval &= ~(field_mask << shift); + curval |= value << shift; + mmio_write(reg, curval); + + return 1; +} + +unsigned int gpio_set (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPSET0, 1, GPIO_MAX_PIN); } +unsigned int gpio_clear (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPCLR0, 1, GPIO_MAX_PIN); } +unsigned int gpio_pull (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPPUPPDN0, 2, GPIO_MAX_PIN); } +unsigned int gpio_function(unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPFSEL0, 3, GPIO_MAX_PIN); } + +void gpio_useAsAlt3(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_ALT3); +} + +void gpio_useAsAlt5(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_ALT5); +} + +void gpio_initOutputPinWithPullNone(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_OUT); +} + +void gpio_setPinOutputBool(unsigned int pin_number, unsigned int onOrOff) { + if (onOrOff) { + gpio_set(pin_number, 1); + } else { + gpio_clear(pin_number, 1); + } +} + +// UART + +enum { + AUX_BASE = PERIPHERAL_BASE + 0x215000, + AUX_IRQ = AUX_BASE, + AUX_ENABLES = AUX_BASE + 4, + AUX_MU_IO_REG = AUX_BASE + 64, + AUX_MU_IER_REG = AUX_BASE + 68, + AUX_MU_IIR_REG = AUX_BASE + 72, + AUX_MU_LCR_REG = AUX_BASE + 76, + AUX_MU_MCR_REG = AUX_BASE + 80, + AUX_MU_LSR_REG = AUX_BASE + 84, + AUX_MU_MSR_REG = AUX_BASE + 88, + AUX_MU_SCRATCH = AUX_BASE + 92, + AUX_MU_CNTL_REG = AUX_BASE + 96, + AUX_MU_STAT_REG = AUX_BASE + 100, + AUX_MU_BAUD_REG = AUX_BASE + 104, + AUX_UART_CLOCK = 500000000, + UART_MAX_QUEUE = 16 * 1024 +}; + +#define AUX_MU_BAUD(baud) ((AUX_UART_CLOCK/(baud*8))-1) + +unsigned char uart_output_queue[UART_MAX_QUEUE]; +unsigned int uart_output_queue_write = 0; +unsigned int uart_output_queue_read = 0; + +void uart_init() { + mmio_write(AUX_ENABLES, 1); //enable UART1 + mmio_write(AUX_MU_IER_REG, 0); + mmio_write(AUX_MU_CNTL_REG, 0); + mmio_write(AUX_MU_LCR_REG, 3); //8 bits + mmio_write(AUX_MU_MCR_REG, 0); + mmio_write(AUX_MU_IER_REG, 0); + mmio_write(AUX_MU_IIR_REG, 0xC6); //disable interrupts + mmio_write(AUX_MU_BAUD_REG, AUX_MU_BAUD(115200)); + gpio_useAsAlt5(14); + gpio_useAsAlt5(15); + mmio_write(AUX_MU_CNTL_REG, 3); //enable RX/TX +} + +unsigned int uart_isOutputQueueEmpty() { + return uart_output_queue_read == uart_output_queue_write; +} + +unsigned int uart_isReadByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x01; } +unsigned int uart_isWriteByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x20; } + +unsigned char uart_readByte() { + while (!uart_isReadByteReady()); + return (unsigned char)mmio_read(AUX_MU_IO_REG); +} + +void uart_writeByteBlockingActual(unsigned char ch) { + while (!uart_isWriteByteReady()); + mmio_write(AUX_MU_IO_REG, (unsigned int)ch); +} + +void uart_loadOutputFifo() { + while (!uart_isOutputQueueEmpty() && uart_isWriteByteReady()) { + uart_writeByteBlockingActual(uart_output_queue[uart_output_queue_read]); + uart_output_queue_read = (uart_output_queue_read + 1) & (UART_MAX_QUEUE - 1); // Don't overrun + } +} + +void uart_writeByteBlocking(unsigned char ch) { + unsigned int next = (uart_output_queue_write + 1) & (UART_MAX_QUEUE - 1); // Don't overrun + + while (next == uart_output_queue_read) uart_loadOutputFifo(); + + uart_output_queue[uart_output_queue_write] = ch; + uart_output_queue_write = next; +} + +void uart_writeText(char *buffer) { + while (*buffer) { + if (*buffer == '\n') uart_writeByteBlocking('\r'); + uart_writeByteBlocking(*buffer++); + } +} + +void uart_drainOutputQueue() { + while (!uart_isOutputQueueEmpty()) uart_loadOutputFifo(); +} + +void uart_update() { + uart_loadOutputFifo(); + + if (uart_isReadByteReady()) { + unsigned char ch = uart_readByte(); + if (ch == '\r') uart_writeText("\n"); else uart_writeByteBlocking(ch); + } +} diff --git a/part6-breakout/io.h b/part6-breakout/io.h new file mode 100644 index 0000000..2a412e6 --- /dev/null +++ b/part6-breakout/io.h @@ -0,0 +1,11 @@ +#define PERIPHERAL_BASE 0xFE000000 + +void uart_init(); +void uart_writeText(char *buffer); +void uart_loadOutputFifo(); +unsigned char uart_readByte(); +unsigned int uart_isReadByteReady(); +void uart_writeByteBlocking(unsigned char ch); +void uart_update(); +void mmio_write(long reg, unsigned int val); +unsigned int mmio_read(long reg); diff --git a/part6-breakout/kernel.c b/part6-breakout/kernel.c new file mode 100644 index 0000000..ecc190b --- /dev/null +++ b/part6-breakout/kernel.c @@ -0,0 +1,10 @@ +#include "io.h" +#include "fb.h" +#include "breakout.h" + +void main() +{ + fb_init(); + game(); + while (1); +} diff --git a/part6-breakout/link.ld b/part6-breakout/link.ld new file mode 100644 index 0000000..dfbf022 --- /dev/null +++ b/part6-breakout/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x80000; /* Kernel load address for AArch64 */ + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; diff --git a/part6-breakout/mb.c b/part6-breakout/mb.c new file mode 100644 index 0000000..f9fb7a0 --- /dev/null +++ b/part6-breakout/mb.c @@ -0,0 +1,39 @@ +#include "io.h" + +// The buffer must be 16-byte aligned as only the upper 28 bits of the address can be passed via the mailbox +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +enum { + VIDEOCORE_MBOX = (PERIPHERAL_BASE + 0x0000B880), + MBOX_READ = (VIDEOCORE_MBOX + 0x0), + MBOX_POLL = (VIDEOCORE_MBOX + 0x10), + MBOX_SENDER = (VIDEOCORE_MBOX + 0x14), + MBOX_STATUS = (VIDEOCORE_MBOX + 0x18), + MBOX_CONFIG = (VIDEOCORE_MBOX + 0x1C), + MBOX_WRITE = (VIDEOCORE_MBOX + 0x20), + MBOX_RESPONSE = 0x80000000, + MBOX_FULL = 0x80000000, + MBOX_EMPTY = 0x40000000 +}; + +unsigned int mbox_call(unsigned char ch) +{ + // 28-bit address (MSB) and 4-bit value (LSB) + unsigned int r = ((unsigned int)((long) &mbox) &~ 0xF) | (ch & 0xF); + + // Wait until we can write + while (mmio_read(MBOX_STATUS) & MBOX_FULL); + + /* write the address of our message to the mailbox with channel identifier */ + mmio_write(MBOX_WRITE, r); + + while (1) { + // Is there a reply? + while (mmio_read(MBOX_STATUS) & MBOX_EMPTY); + + // Is it a reply to our message? + if (r == mmio_read(MBOX_READ)) return mbox[1]==MBOX_RESPONSE; // Is it successful? + + } + return 0; +} diff --git a/part6-breakout/mb.h b/part6-breakout/mb.h new file mode 100644 index 0000000..0e1861b --- /dev/null +++ b/part6-breakout/mb.h @@ -0,0 +1,34 @@ +extern volatile unsigned int mbox[36]; + +enum { + MBOX_REQUEST = 0 +}; + +enum { + MBOX_CH_POWER = 0, + MBOX_CH_FB = 1, + MBOX_CH_VUART = 2, + MBOX_CH_VCHIQ = 3, + MBOX_CH_LEDS = 4, + MBOX_CH_BTNS = 5, + MBOX_CH_TOUCH = 6, + MBOX_CH_COUNT = 7, + MBOX_CH_PROP = 8 // Request from ARM for response by VideoCore +}; + +enum { + MBOX_TAG_SETPOWER = 0x28001, + MBOX_TAG_SETCLKRATE = 0x38002, + + MBOX_TAG_SETPHYWH = 0x48003, + MBOX_TAG_SETVIRTWH = 0x48004, + MBOX_TAG_SETVIRTOFF = 0x48009, + MBOX_TAG_SETDEPTH = 0x48005, + MBOX_TAG_SETPXLORDR = 0x48006, + MBOX_TAG_GETFB = 0x40001, + MBOX_TAG_GETPITCH = 0x40008, + + MBOX_TAG_LAST = 0 +}; + +unsigned int mbox_call(unsigned char ch); diff --git a/part6-breakout/terminal.h b/part6-breakout/terminal.h new file mode 100644 index 0000000..ef05b85 --- /dev/null +++ b/part6-breakout/terminal.h @@ -0,0 +1,253 @@ +unsigned int vgapal[] = { + 0x000000, + 0x0000AA, + 0x00AA00, + 0x00AAAA, + 0xAA0000, + 0xAA00AA, + 0xAA5500, + 0xAAAAAA, + 0x555555, + 0x5555FF, + 0x55FF55, + 0x55FFFF, + 0xFF5555, + 0xFF55FF, + 0xFFFF55, + 0xFFFFFF +}; + +enum { + FONT_WIDTH = 8, + FONT_HEIGHT = 8, + FONT_BPG = 8, // Bytes per glyph + FONT_BPL = 1, // Bytes per line + FONT_NUMGLYPHS = 224 +}; + +unsigned char font[FONT_NUMGLYPHS][FONT_BPG] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) + { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!) + { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (") + { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#) + { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($) + { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%) + { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&) + { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (') + { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (() + { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ()) + { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*) + { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,) + { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) + { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) + { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) + { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) + { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) + { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) + { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) + { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) + { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) + { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) + { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) + { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;) + { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<) + { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=) + { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>) + { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?) + { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@) + { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) + { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) + { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) + { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) + { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) + { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) + { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) + { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) + { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) + { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) + { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) + { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) + { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) + { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) + { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) + { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) + { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) + { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) + { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) + { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) + { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) + { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) + { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([) + { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\) + { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (]) + { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_) + { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`) + { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) + { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) + { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) + { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) + { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) + { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) + { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) + { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) + { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) + { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) + { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) + { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) + { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) + { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) + { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) + { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) + { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) + { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) + { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) + { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) + { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) + { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({) + { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|) + { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (}) + { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007F + { 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x18, 0x30, 0x1E}, // U+00C7 (C cedille) + { 0x00, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FC (u umlaut) + { 0x38, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E9 (e aigu) + { 0x7E, 0xC3, 0x3C, 0x60, 0x7C, 0x66, 0xFC, 0x00}, // U+00E2 (a circumflex) + { 0x33, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E4 (a umlaut) + { 0x07, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E0 (a grave) + { 0x0C, 0x0C, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E5 (a ring) + { 0x00, 0x00, 0x1E, 0x03, 0x03, 0x1E, 0x30, 0x1C}, // U+00E7 (c cedille) + { 0x7E, 0xC3, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00}, // U+00EA (e circumflex) + { 0x33, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00EB (e umlaut) + { 0x07, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E8 (e grave) + { 0x33, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EF (i umlaut) + { 0x3E, 0x63, 0x1C, 0x18, 0x18, 0x18, 0x3C, 0x00}, // U+00EE (i circumflex) + { 0x07, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EC (i grave) + { 0x63, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x63, 0x00}, // U+00C4 (A umlaut) + { 0x0C, 0x0C, 0x00, 0x1E, 0x33, 0x3F, 0x33, 0x00}, // U+00C5 (A ring) + { 0x07, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00}, // U+00C8 (E grave) + { 0x00, 0x00, 0xFE, 0x30, 0xFE, 0x33, 0xFE, 0x00}, // U+00E6 (ae) + { 0x7C, 0x36, 0x33, 0x7F, 0x33, 0x33, 0x73, 0x00}, // U+00C6 (AE) + { 0x1E, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F4 (o circumflex) + { 0x00, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F6 (o umlaut) + { 0x00, 0x07, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F2 (o grave) + { 0x1E, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FB (u circumflex) + { 0x00, 0x07, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00F9 (u grave) + { 0x00, 0x33, 0x00, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+00FF (y umlaut) + { 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00}, // U+00D6 (O umlaut) + { 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+00DC (U umlaut) + { 0x18, 0x18, 0x7E, 0x03, 0x03, 0x7E, 0x18, 0x18}, // U+00A2 (dollarcents) + { 0x1C, 0x36, 0x26, 0x0F, 0x06, 0x67, 0x3F, 0x00}, // U+00A3 (pound sterling) + { 0x33, 0x33, 0x1E, 0x3F, 0x0C, 0x3F, 0x0C, 0x0C}, // U+00A5 (yen) + { 0x7C, 0xC6, 0x1C, 0x36, 0x36, 0x1C, 0x33, 0x1E}, // U+00A7 (paragraph) + { 0x70, 0xD8, 0x18, 0x3C, 0x18, 0x18, 0x1B, 0x0E}, // U+0192 (dutch florijn) + { 0x38, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E1 (a aigu) + { 0x1C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00ED (i augu) + { 0x00, 0x38, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F3 (o aigu) + { 0x00, 0x38, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FA (u aigu) + { 0x00, 0x1F, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x00}, // U+00F1 (n ~) + { 0x3F, 0x00, 0x33, 0x37, 0x3F, 0x3B, 0x33, 0x00}, // U+00D1 (N ~) + { 0x3C, 0x36, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00}, // U+00AA (superscript a) + { 0x1C, 0x36, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00}, // U+00BA (superscript 0) + { 0x0C, 0x00, 0x0C, 0x06, 0x03, 0x33, 0x1E, 0x00}, // U+00BF (inverted ?) + { 0x00, 0x00, 0x00, 0x3F, 0x03, 0x03, 0x00, 0x00}, // U+2310 (gun pointing right) + { 0x00, 0x00, 0x00, 0x3F, 0x30, 0x30, 0x00, 0x00}, // U+00AC (gun pointing left) + { 0xC3, 0x63, 0x33, 0x7B, 0xCC, 0x66, 0x33, 0xF0}, // U+00BD (1/2) + { 0xC3, 0x63, 0x33, 0xBD, 0xEC, 0xF6, 0xF3, 0x03}, // U+00BC (1/4) + { 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00}, // U+00A1 (inverted !) + { 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00}, // U+00AB (<<) + { 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00}, // U+00BB (>>) + { 0x55, 0x00, 0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00}, // U+2591 (25% solid) + { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA}, // U+2592 (50% solid) + { 0xFF, 0xAA, 0xFF, 0x55, 0xFF, 0xAA, 0xFF, 0x55}, // U+2593 (75% solid) + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, // U+2502 (thin vertical) + { 0x08, 0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x08}, // U+2524 (down L, left L, up L) + { 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2561 (up L, down L, left D) + { 0x14, 0x14, 0x14, 0x14, 0x17, 0x14, 0x14, 0x14}, // U+2562 (up D, down D, left L) + { 0x00, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x14, 0x14}, // U+2556 (down D, left L) + { 0x00, 0x00, 0x00, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2555 (down L, left D) + { 0x14, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14}, // U+2563 (up D, down D, left D) + { 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, // U+2551 (double vertical) + { 0x00, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x14, 0x14}, // U+2557 (down D, left D) + { 0x14, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x00, 0x00}, // U+255D (up D, left D) + { 0x14, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x00, 0x00}, // U+255C (up D, left L) + { 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x00, 0x00}, // U+255B (up L, left D) + { 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08}, // U+2510 (down L, left L) + { 0x08, 0x08, 0x08, 0x08, 0xf8, 0x00, 0x00, 0x00}, // U+2514 (up L, right L) + { 0x08, 0x08, 0x08, 0x08, 0xff, 0x00, 0x00, 0x00}, // U+2534 (up L, right L, left L) + { 0x00, 0x00, 0x00, 0x00, 0xff, 0x08, 0x08, 0x08}, // U+252C (down L, right L, left L) + { 0x08, 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, 0x08}, // U+251C (down L, right L, up L) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00}, // U+2500 (thin horizontal) + { 0x08, 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08}, // U+253C (up L, right L, left L, down L) + { 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+255E (up L, down L, right D) + { 0x14, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x14, 0x14}, // U+255F (up D, down D, right L) + { 0x14, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x00, 0x00}, // U+255A (up D, right D) + { 0x00, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14}, // U+2554 (down D, right D) + { 0x14, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00}, // U+2569 (left D, right D, up D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14}, // U+2566 (left D, right D, down D) + { 0x14, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x14, 0x14}, // U+2560 (up D, down D, right D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2550 (double horizontal) + { 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14}, // U+256C (left D, right D, down D, up D) + { 0x08, 0x08, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2567 (left D, right D, up L) + { 0x14, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x00, 0x00}, // U+2568 (left L, right L, up D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x08, 0x08}, // U+2564 (left D, right D, down L) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x14, 0x14}, // U+2565 (left L, right L, down D) + { 0x14, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x00, 0x00}, // U+2559 (up D, right L) + { 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x00, 0x00}, // U+2558 (up L, right D) + { 0x00, 0x00, 0x00, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+2552 (down L, right D) + { 0x00, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x14, 0x14}, // U+2553 (down D, right L) + { 0x14, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x14, 0x14}, // U+256B (left L, right L, down D, up D) + { 0x08, 0x08, 0x08, 0xFF, 0x08, 0xFF, 0x08, 0x08}, // U+256A (left D, right D, down L, up L) + { 0x08, 0x08, 0x08, 0x08, 0x0f, 0x00, 0x00, 0x00}, // U+2518 (up L, left L) + { 0x00, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08}, // U+250C (down L, right L) + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2588 (solid) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2584 (bottom half) + { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, // U+258C (left half) + { 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, // U+2590 (right half) + { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, // U+2580 (top half) +};