commit: | a3cf8b5eb2f6f8f4384153d820702f0401ac2d28 |
author: | Chip |
committer: | Chip |
date: | Fri Feb 22 23:35:55 2019 -0600 |
parents: |
diff --git a/.cargo/config b/.cargo/config line changes: +33/-0 index 0000000..fa909f5 --- /dev/null +++ b/.cargo/config
@@ -0,0 +1,33 @@ +[target.thumbv7m-none-eabi] +# uncomment this to make `cargo run` execute programs on QEMU +# runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# uncomment ONE of these three option to make `cargo run` start a GDB session +# which option to pick depends on your system +# runner = "arm-none-eabi-gdb -q -x openocd.gdb" +# runner = "gdb-multiarch -q -x openocd.gdb" +# runner = "gdb -q -x openocd.gdb" + +rustflags = [ + # LLD (shipped with the Rust toolchain) is used as the default linker + "-C", "link-arg=-Tlink.x", + + # if you run into problems with LLD switch to the GNU linker by commenting out + # this line + # "-C", "linker=arm-none-eabi-ld", + + # if you need to link to pre-compiled C libraries provided by a C toolchain + # use GCC as the linker by commenting out both lines above and then + # uncommenting the three lines below + # "-C", "linker=arm-none-eabi-gcc", + # "-C", "link-arg=-Wl,-Tlink.x", + # "-C", "link-arg=-nostartfiles", +] + +[build] +# Pick ONE of these compilation targets +# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ +target = "thumbv7m-none-eabi" # Cortex-M3 +# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) +# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
diff --git a/.gitignore b/.gitignore line changes: +7/-0 index 0000000..a299401 --- /dev/null +++ b/.gitignore
@@ -0,0 +1,7 @@ +**/*.rs.bk +.#* +.gdb_history +.cargo-ok +Cargo.lock +target/ +libs/
diff --git a/Cargo.toml b/Cargo.toml line changes: +40/-0 index 0000000..870df26 --- /dev/null +++ b/Cargo.toml
@@ -0,0 +1,40 @@ +[package] +authors = ["Chip <bytex64@bytex64.net>"] +edition = "2018" +readme = "README.md" +name = "hello-m" +version = "0.1.0" + +[dependencies] +embedded-hal = "0.2.2" +cortex-m = { version = "0.5.8", features = [ "const-fn" ] } +cortex-m-rt = "0.6.6" +atsam3xa = "0.1.0" +panic-halt = "0.2.0" +cty = "0.1.5" +nb = "0.1.1" +arraydeque = { version = "0.4.3", default-features = false } +alloc-cortex-m = "0.3.5" +r0 = "0.2.2" + +[dependencies.atsam3xa-hal] +version = "0.1.0" +features = ["rt"] + +[patch.crates-io] +atsam3xa = { path = "../atsam3xa" } +atsam3xa-hal = { path = "../atsam3xa-hal" } +cortex-m-rt = { path = "../cortex-m-rt" } + +# this lets you use `cargo fix`! +[[bin]] +name = "hello-m" +test = false +bench = false + +[profile.release] +opt-level = "z" +codegen-units = 1 # better optimizations +debug = true # symbols are nice and they don't increase the size on Flash +incremental = false +lto = true # better optimizations
diff --git a/README.md b/README.md line changes: +70/-0 index 0000000..89a53f6 --- /dev/null +++ b/README.md
@@ -0,0 +1,70 @@ +# `hello-m` + +A simple microcontroller hacking environment + +## Building + +Not gonna lie, this is a terrible mess right now. + +Install a copy of the GNU Arm Embedded Toolchain and put it in your +path. You can get `gcc-arm-none-eabi` on debian-style systems, but I +recommend getting a newer version. + +Run `./fetch-external.sh`. That should retrieve the external +dependencies and patch them. + +Run `./build-external.sh`. This should produce `libc.a`, `libm.a`, and +`liblua.a` in `libs/`. + +Cd to `shell` and run `make`. This will produce `libshell.a`. + +Finally, run `cargo build`. If you want NTSC video, that currently only +works in release mode with `--release`. + +## Using + +Flash to your Due and connect to the serial port (the programming port, +not the native port). You should get a Lua shell. + +### peek and poke + +Two utility functions are provided, `peek(addr)` and `poke(addr, val)`, +which will read or write 32-bit memory values. + +### memory map + +The Due's 96KB is split into a 64KB user segment, and a 32KB kernel +segment. The user segment is empty and can be fully utilized by +application software. The kernel segment is further divided into a +public and private portion. The public portion is accessible to user +programs and contains data supporting application services like the +memory allocator state, and publicly accessible service state like video +RAM. The private portion contains management data for system services +and the main stack for exception handling. + +In user mode, the MPU pokes out several holes in the address space. + +0x00000000 +------------------+ + | ROM | +0x00080000 +------------------+ + ... +0x20000000 +------------------+ + | User RAM | +0x20010000 +------------------+ + ... +0x2001F000 +------------------+ + | User Stack | (mirrored User RAM) +0x20020000 +------------------+ + ... +0x20080000 +------------------+ + | Public Kernel | +0x20084000 +------------------+ + | Private Kernel | +0x20088000 +------------------+ + ... +0x40000000 +------------------+ + | Peripherals | +0x60000000 +------------------+ + +There's of course a lot more to the memory map, and I recommend perusing +the [ATSAM3X/A datasheet](http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf) for more information.
diff --git a/build-external.sh b/build-external.sh line changes: +25/-0 index 0000000..f687093 --- /dev/null +++ b/build-external.sh
@@ -0,0 +1,25 @@ +#!/bin/sh +set -e +set -x +ROOT=$PWD + +mkdir -p libs + +cd $ROOT/external/newlib +mkdir build +cd build +../configure --target=arm-none-eabi --enable-newlib-reent-small --disable-newlib-fvwrite-in-streamio --disable-newlib-fseek-optimization --disable-newlib-wide-orient --disable-newlib-unbuf-stream-opt --enable-lite-exit --enable-newlib-global-atexit --enable-newlib-nano-formatted-io --disable-nls --disable-newlib-multithread --disable-multilib --disable-newlib-supplied-syscalls CFLAGS_FOR_TARGET='-g -mthumb -march=armv7-m -DMALLOC_PROVIDED' +#--enable-target-optspace +make configure-target-newlib +# Remove conflicting builtin/rust stdlib symbols +sed -i.bak -e 's/lib_a-\(memcmp\|memcpy\|memmove\|memset\)\.\$(OBJEXT) //g' arm-none-eabi/newlib/libc/string/Makefile +sed -i.bak -e 's/lib_a-\(w_fmod\|wf_fmod\)\.\$(OBJEXT) //g' arm-none-eabi/newlib/libm/math/Makefile +sed -i.bak -e 's/lib_a-memcpy\.\$(OBJEXT) //g' arm-none-eabi/newlib/libc/machine/arm/Makefile +make -j4 all-target-newlib +cd arm-none-eabi/newlib +# copy libc/libc.a, which doesn't include libm.a symbols +cp libc/libc.a libm.a $ROOT/libs/ + +cd $ROOT/external/lua-5.3.5/src +make -j4 CC='arm-none-eabi-gcc -mthumb -march=armv7-m' liblua.a +cp liblua.a $ROOT/libs/
diff --git a/build.rs b/build.rs line changes: +32/-0 index 0000000..8cf739c --- /dev/null +++ b/build.rs
@@ -0,0 +1,32 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + // Put the linker script somewhere the linker can find it + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // Build the shell + //Command::new("make").args(&["-C", "shell"]).status().unwrap(); + + println!("cargo:rerun-if-changed=memory.x"); + println!("cargo:rerun-if-changed=shell/libshell.a"); + println!("cargo:rerun-if-changed=libs/libc.a"); + println!("cargo:rerun-if-changed=libs/libm.a"); + println!("cargo:rerun-if-changed=libs/liblua.a"); + + println!("cargo:rustc-link-search=libs"); + println!("cargo:rustc-link-search=shell"); + + println!("cargo:rustc-link-lib=static=c"); + println!("cargo:rustc-link-lib=static=m"); + println!("cargo:rustc-link-lib=static=lua"); + println!("cargo:rustc-link-lib=static=shell"); +}
diff --git a/crt/include/sysops.h b/crt/include/sysops.h line changes: +34/-0 index 0000000..2c02779 --- /dev/null +++ b/crt/include/sysops.h
@@ -0,0 +1,34 @@ +#ifndef __SYSOPS_H +#define __SYSOPS_H + +#include <stdint.h> +#include <stddef.h> + +struct console_operations { + int32_t (*readb)(); + size_t (*read)(uint8_t buf[], size_t len); + size_t (*readline)(uint8_t buf[], size_t len); + void (*writeb)(uint8_t b); + void (*write)(uint8_t a[], size_t len); + void (*discard)(); + void (*pos)(size_t x, size_t y); + void (*clear)(); +}; + +struct alloc_operations { + void (*init)(size_t start, size_t size); + void * (*alloc)(size_t size, size_t align); + void (*dealloc)(void *ptr, size_t size, size_t align); + void * (*malloc)(size_t size); + void (*free)(void *ptr); + void * (*realloc)(void *ptr, size_t size); +}; + +struct sys_ops { + struct console_operations *console; + struct alloc_operations *alloc; +}; + +static struct sys_ops *sys_ops __attribute__((section (".rodata"))) = (struct sys_ops *) 0xfc; + +#endif //__SYSOPS_H
diff --git a/external/.gitignore b/external/.gitignore line changes: +2/-0 index 0000000..bdbcd84 --- /dev/null +++ b/external/.gitignore
@@ -0,0 +1,2 @@ +lua-5.* +newlib
diff --git a/external/lua.patch b/external/lua.patch line changes: +37/-0 index 0000000..5471b69 --- /dev/null +++ b/external/lua.patch
@@ -0,0 +1,37 @@ +diff -ur lua-5.3.5/src/luaconf.h lua-5.3.5.orig/src/luaconf.h +--- lua-5.3.5/src/luaconf.h 2017-04-19 12:29:57.000000000 -0500 ++++ lua-5.3.5.orig/src/luaconf.h 2019-01-10 23:01:29.048803300 -0600 +@@ -33,7 +33,7 @@ + ** ensure that all software connected to Lua will be compiled with the + ** same configuration. + */ +-/* #define LUA_32BITS */ ++#define LUA_32BITS + + + /* +@@ -782,7 +782,7 @@ + ** without modifying the main part of the file. + */ + +- ++#define l_signalT unsigned int + + + +Only in lua-5.3.5.orig/src: lundump.o +Only in lua-5.3.5.orig/src: lutf8lib.o +Only in lua-5.3.5.orig/src: lvm.o +Only in lua-5.3.5.orig/src: lzio.o +diff -ur lua-5.3.5/src/Makefile lua-5.3.5.orig/src/Makefile +--- lua-5.3.5/src/Makefile 2018-06-25 12:46:36.000000000 -0500 ++++ lua-5.3.5.orig/src/Makefile 2019-01-21 23:54:50.177074600 -0600 +@@ -7,7 +7,7 @@ + PLAT= none + + CC= gcc -std=gnu99 +-CFLAGS= -O2 -Wall -Wextra -DLUA_COMPAT_5_2 $(SYSCFLAGS) $(MYCFLAGS) ++CFLAGS= -O2 -g -Wall -Wextra -DLUA_COMPAT_5_2 $(SYSCFLAGS) $(MYCFLAGS) + LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS) + LIBS= -lm $(SYSLIBS) $(MYLIBS) +
diff --git a/external/newlib.patch b/external/newlib.patch line changes: +11/-0 index 0000000..f72f85e --- /dev/null +++ b/external/newlib.patch
@@ -0,0 +1,11 @@ +diff --git a/newlib/libc/stdlib/mallocr.c b/newlib/libc/stdlib/mallocr.c +index 26d1c89cc..6e4bd78d4 100644 +--- a/newlib/libc/stdlib/mallocr.c ++++ b/newlib/libc/stdlib/mallocr.c +@@ -1,5 +1,5 @@ + #ifdef MALLOC_PROVIDED +-int _dummy_mallocr = 1; ++int __attribute__((weak)) _dummy_mallocr = 1; + #else + /* ---------- To make a malloc.h, start cutting here ------------ */ +
diff --git a/fetch-external.sh b/fetch-external.sh line changes: +20/-0 index 0000000..75ecc01 --- /dev/null +++ b/fetch-external.sh
@@ -0,0 +1,20 @@ +#!/bin/sh + +set -e +set -x + +cd external + +wget -c https://www.lua.org/ftp/lua-5.3.5.tar.gz +tar xfz lua-5.3.5.tar.gz +( + cd lua-5.3.5 + patch -p1 <../lua.patch +) + +git clone git://sourceware.org/git/newlib-cygwin.git newlib +( + cd newlib + git reset --hard 9fa22dba558f5e3efd0bb719049491edcd7b5e0b + patch -p1 <../newlib.patch +)
diff --git a/memory.x b/memory.x line changes: +83/-0 index 0000000..e5728d0 --- /dev/null +++ b/memory.x
@@ -0,0 +1,83 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x00000000, LENGTH = 512K + RAM : ORIGIN = 0x20084000, LENGTH = 16K + APPRAM : ORIGIN = 0x20000000, LENGTH = 64K + KGRAM : ORIGIN = 0x20080000, LENGTH = 16K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Example of putting non-initialized variables into custom RAM locations. */ +/* This assumes you have defined a region RAM2 above, and in the Rust + sources added the attribute `#[link_section = ".ram2bss"]` to the data + you want to place there. */ +/* Note that the section will not be zero-initialized by the runtime! */ +/* SECTIONS { + .ram2bss (NOLOAD) : ALIGN(4) { + *(.ram2bss); + . = ALIGN(4); + } > RAM2 + } INSERT AFTER .bss; +*/ + +_appram = ORIGIN(APPRAM); +_appram_end = ORIGIN(APPRAM) + LENGTH(APPRAM); + +SECTIONS { + .driver_interfaces : ALIGN(4) { + *(.driver_interfaces); + . = ALIGN(4); + } > FLASH + + .init_array : ALIGN(4) { + *(.init_array); + . = ALIGN(4); + } > FLASH + + PROVIDE(_stext = ADDR(.init_array) + SIZEOF(.init_array)); +} INSERT AFTER .vector_table; + +SECTIONS { + .kgdata : ALIGN(4) { + *(.kgdata); + *libc.a*(.data .data.*); + *libm.a*(.data .data.*); + *liblua.a*(.data .data.*); + *libshell.a*(.data .data.*); + . = ALIGN(4); + } > KGRAM AT >FLASH + + /* .kgdata ROM location */ + __skgdata = ADDR(.kgdata); + __ekgdata = ADDR(.kgdata) + SIZEOF(.kgdata); + + /* .kgdata RAM location */ + __sikgdata = LOADADDR(.kgdata); +} INSERT BEFORE .data; + +SECTIONS { + .kgbss : ALIGN(4) { + *(.kgbss); + *libc.a*(.bss .bss.* COMMON); + *libm.a*(.bss .bss.* COMMON); + *liblua.a*(.bss .bss.* COMMON); + *libshell.a*(.bss .bss.* COMMON); + . = ALIGN(4); + } > KGRAM AT >KGRAM /* Don't ask me why, but simply "> KGRAM" doesn't work */ + + __skgbss = ADDR(.kgbss); + __ekgbss = ADDR(.kgbss) + SIZEOF(.kgbss); +} INSERT BEFORE .bss;
diff --git a/shell/.gitignore b/shell/.gitignore line changes: +2/-0 index 0000000..9eca6c8 --- /dev/null +++ b/shell/.gitignore
@@ -0,0 +1,2 @@ +*.a +*.o
diff --git a/shell/Makefile b/shell/Makefile line changes: +8/-0 index 0000000..77862c9 --- /dev/null +++ b/shell/Makefile
@@ -0,0 +1,8 @@ +INCLUDES = -I../crt/include -I../external/lua-5.3.5/src +CFLAGS = -std=c11 -march=armv7-m -mthumb -msingle-pic-base -Os -g -Wall $(INCLUDES) + +all: system.o main.o + ar cr libshell.a $^ + +%.o: %.c + arm-none-eabi-gcc $(CFLAGS) -c $<
diff --git a/shell/main.c b/shell/main.c line changes: +137/-0 index 0000000..7d43bfe --- /dev/null +++ b/shell/main.c
@@ -0,0 +1,137 @@ +#include <sysops.h> +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> + +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> + +struct MemoryRange { + uint32_t start; + uint32_t length; +}; + +void svc64(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3); + +__asm__( +".global svc64\n" +".type svc64,function\n" +"svc64:\n" +" svc #64\n" +" bx lr\n" +); + +int peek(lua_State *L) { + int n_args = lua_gettop(L); + if (n_args != 1) { + puts("peek(addr)"); + return 0; + } + uint32_t addr = lua_tointeger(L, 1); + uint32_t data = *(uint32_t *)addr; + lua_pushinteger(L, data); + return 1; +} + +int poke(lua_State *L) { + int n_args = lua_gettop(L); + if (n_args != 2) { + puts("poke(addr, value)"); + return 0; + } + uint32_t addr = lua_tointeger(L, 1); + uint32_t data = lua_tointeger(L, 2); + *(uint32_t *)addr = data; + return 0; +} + +void shell_main(struct MemoryRange *r) { + lua_State *L; + char buffer[128]; + + sys_ops->alloc->init(r->start, r->length); + + L = luaL_newstate(); + if (L == NULL) { + puts("Could not allocate lua state\r\n"); + exit(EXIT_FAILURE); + } + + luaL_openlibs(L); + + lua_pushcfunction(L, peek); + lua_setglobal(L, "peek"); + lua_pushcfunction(L, poke); + lua_setglobal(L, "poke"); + + puts(LUA_VERSION " Okay!\n"); + + while(1) { + fputs("> ", stdout); + fflush(stdout); + + uint32_t n = sys_ops->console->readline((uint8_t *)buffer, 127); + buffer[n] = 0; + puts(""); + + if (strcmp(buffer, "clear") == 0) { + sys_ops->console->clear(); + } else if (strcmp(buffer, "svcall") == 0) { + svc64(42, 69, 88, 64); + } else if (strcmp(buffer, "malloc") == 0) { + char *s = sys_ops->alloc->malloc(1024); + if (!s) { + puts("alloc failed"); + continue; + } + const char hello[] = "Hello alloc!"; + for (int i = 0; i < strlen(hello); i++) { + s[i] = hello[i]; + } + } else if (strcmp(buffer, "dbz") == 0) { + // Enable DIV_0_TRP + *(uint32_t*)0xe000ed14 = 0x10; + __asm__( + "mov r0, #1\n" + "mov r1, #0\n" + "sdiv r0, r0, r1\n" + ); + } else { + luaL_loadstring(L, buffer); + int r = lua_pcall(L, 0, LUA_MULTRET, 0); + if (r != 0) { + fputs("error: ", stderr); + switch(r) { + case LUA_ERRRUN: + break; + case LUA_ERRMEM: + puts("memory allocation"); + break; + case LUA_ERRERR: + puts("message handler"); + break; + case LUA_ERRGCMM: + puts("__gc metamethod"); + break; + default: + puts("unknown"); + } + int top = lua_gettop(L); + puts(luaL_tolstring(L, top, NULL)); + } else { + int top = lua_gettop(L); + if (top == 1) { + puts(luaL_tolstring(L, 1, NULL)); + } else { + for (int i = 1; i <= top; i++) { + printf("%d: %s\n", i, luaL_tolstring(L, i, NULL)); + } + } + } + + lua_settop(L, 0); + } + } +}
diff --git a/shell/system.c b/shell/system.c line changes: +118/-0 index 0000000..c77bdac --- /dev/null +++ b/shell/system.c
@@ -0,0 +1,118 @@ +#include <sysops.h> + +#include <reent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/times.h> + +#include <errno.h> +#undef errno +extern int errno; + +void _exit(int status) { + puts("No exit!\r\n"); + while (1) { } +} + +int _close(int file) { + return -1; +} + +int _fstat(int file, struct stat *st) { + st->st_mode = S_IFCHR; + return 0; +} + +int _gettimeofday(struct timeval *tv, struct timezone *tz) { + errno = EINVAL; + return -1; +}; + +int _isatty(int fd) { + if (fd < 3) + return 1; + errno = EBADF; + return 0; +} + +int _getpid(void) { + return 1; +} + +int _kill(int pid, int sig) { + errno = EINVAL; + return -1; +} + +int _link(char *old, char *new) { + errno = EMLINK; + return -1; +} + +int _lseek(int file, int ptr, int dir) { + return 0; +} + +int _open(const char *name, int flags, int mode) { + return -1; +} + +int _read(int file, char *ptr, int len) { + return 0; +} + +int _times(struct tms *buf) { + return -1; +} + +int _unlink(char *name) { + errno = ENOENT; + return -1; +} + +int _write(int file, char *ptr, int len) { + if (file == 1 || file == 2) { + sys_ops->console->write((uint8_t *)ptr, len); + return len; + } else { + errno = EBADF; + return -1; + } +} + +void * malloc(size_t nbytes) { + return sys_ops->alloc->malloc(nbytes); +} + +void * _malloc_r(struct _reent *r, size_t nbytes) { + return malloc(nbytes); +} + +void * calloc(size_t n, size_t elem_size) { + void *ptr = malloc(n * elem_size); + memset(ptr, 0, n * elem_size); + return ptr; +} + +void * _calloc_r(struct _reent *r, size_t n, size_t elem_size) { + return calloc(n, elem_size); +} + +void free(void *ptr) { + sys_ops->alloc->free(ptr); +} + +void _free_r(struct _reent *r, void *ptr) { + free(ptr); +} + +void * realloc(void *ptr, size_t nbytes) { + return sys_ops->alloc->realloc(ptr, nbytes); +} + +void * _realloc_r(struct _reent *r, void *ptr, size_t nbytes) { + return realloc(ptr, nbytes); +}
diff --git a/src/alloc.rs b/src/alloc.rs line changes: +187/-0 index 0000000..8599f9e --- /dev/null +++ b/src/alloc.rs
@@ -0,0 +1,187 @@ +extern crate alloc; +use core::alloc::{GlobalAlloc, Layout}; +use alloc::collections::BTreeMap; +use crate::console::ConsoleOutput; +use crate::ALLOCATOR; + +pub unsafe fn init(start: usize, size: usize) { + ALLOCATOR.init(start, size); +} + +/* +#[cfg(not(alloc_trace))] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + ALLOCATOR.alloc(layout) +} +*/ + +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + let ptr = ALLOCATOR.alloc(layout); + #[cfg(alloc_trace)] + { + crate::serial_console::get_global_console().write("+"); + crate::util::print_hex_padded(ptr as u32, 8); + crate::serial_console::get_global_console().write(" "); + } + ptr +} + +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + #[cfg(alloc_trace)] + { + crate::serial_console::get_global_console().write("-"); + crate::util::print_hex_padded(ptr as u32, 8); + crate::serial_console::get_global_console().write(" "); + } + ALLOCATOR.dealloc(ptr, layout); +} + +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + #[cfg(alloc_trace)] + { + crate::serial_console::get_global_console().write("="); + crate::util::print_hex_padded(ptr as u32, 8); + } + let ptr = ALLOCATOR.realloc(ptr, layout, new_size); + #[cfg(alloc_trace)] + { + crate::serial_console::get_global_console().write("/"); + crate::util::print_hex_padded(ptr as u32, 8); + crate::serial_console::get_global_console().write(" "); + } + ptr +} + +pub struct RustAllocOperations { + pub init: unsafe fn(start: usize, size: usize), + pub alloc: unsafe fn(layout: Layout) -> *mut u8, + pub dealloc: unsafe fn(ptr: *mut u8, layout: Layout), + pub realloc: unsafe fn(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8, +} + +pub const RUST_ALLOC_OPERATIONS: RustAllocOperations = RustAllocOperations { + init, + alloc, + dealloc, + realloc, +}; + +pub unsafe extern "C" fn c_init(start: usize, size: usize) { + init(start, size) +} + +pub unsafe extern "C" fn c_alloc(size: usize, align: usize) -> *mut u8 { + let layout = match Layout::from_size_align(size, align) { + Ok(l) => l, + Err(_) => return 0 as *mut u8, + }; + + alloc(layout) +} + +pub unsafe extern "C" fn c_dealloc(ptr: *mut u8, size: usize, align: usize) { + let layout = match Layout::from_size_align(size, align) { + Ok(l) => l, + Err(_) => return, + }; + + dealloc(ptr, layout) +} + +#[link_section = ".kgbss"] +static mut MALLOC_MAP: Option<BTreeMap<*mut u8, usize>> = None; + +pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 { + let layout = match Layout::from_size_align(size, 4) { + Ok(l) => l, + Err(_) => return 0 as *mut u8, + }; + + if MALLOC_MAP.is_none() { + MALLOC_MAP = Some(BTreeMap::new()); + } + + let ptr = alloc(layout); + if ptr == (0 as *mut u8) { + return 0 as *mut u8; + } + + let map = MALLOC_MAP.as_mut().unwrap(); + map.insert(ptr, size); + + ptr +} + +pub unsafe extern "C" fn free(ptr: *mut u8) { + if ptr.is_null() { + return; + } + + if MALLOC_MAP.is_none() { + return; + } + + let map = MALLOC_MAP.as_mut().unwrap(); + if map.contains_key(&ptr) { + let size = *map.get(&ptr).unwrap(); + let layout = match Layout::from_size_align(size, 4) { + Ok(l) => l, + Err(_) => return, + }; + dealloc(ptr, layout); + map.remove(&ptr); + } +} + +pub unsafe extern "C" fn c_realloc(ptr: *mut u8, nbytes: usize) -> *mut u8 { + if MALLOC_MAP.is_none() { + MALLOC_MAP = Some(BTreeMap::new()); + } + + if ptr as u32 == 0 { + return malloc(nbytes); + } else if nbytes == 0 { + free(ptr); + return 0 as *mut u8; + } + + let map = MALLOC_MAP.as_mut().unwrap(); + if map.contains_key(&ptr) { + let size = *map.get(&ptr).unwrap(); + let layout = match Layout::from_size_align(size, 4) { + Ok(l) => l, + Err(_) => return 0 as *mut u8, + }; + let new_ptr = realloc(ptr, layout, nbytes); + map.remove(&ptr); + map.insert(new_ptr, nbytes); + new_ptr + } else { + 0 as *mut u8 + } +} + +#[repr(C)] +pub struct CAllocOperations { + pub init: unsafe extern "C" fn(start: usize, size: usize), + pub alloc: unsafe extern "C" fn(size: usize, align: usize) -> *mut u8, + pub dealloc: unsafe extern "C" fn(ptr: *mut u8, size: usize, align: usize), + pub malloc: unsafe extern "C" fn(size: usize) -> *mut u8, + pub free: unsafe extern "C" fn(ptr: *mut u8), + pub realloc: unsafe extern "C" fn(ptr: *mut u8, nbytes: usize) -> *mut u8, +} + +pub const C_ALLOC_OPERATIONS: CAllocOperations = CAllocOperations { + init: c_init, + alloc: c_alloc, + dealloc: c_dealloc, + malloc, + free, + realloc: c_realloc, +}; + +#[alloc_error_handler] +pub fn handle_alloc_error(_layout: Layout) -> ! { + crate::serial_console::get_global_console().write("OOM!\r\n"); + loop{} +}
diff --git a/src/console.rs b/src/console.rs line changes: +90/-0 index 0000000..60057e9 --- /dev/null +++ b/src/console.rs
@@ -0,0 +1,90 @@ +use arraydeque::ArrayDeque; +use cortex_m::interrupt; + +pub trait ConsoleInput { + fn readb(&mut self) -> Option<u8>; + fn read(&mut self, buf: &mut [u8]) -> usize; + fn discard(&mut self); +} + +pub trait ConsoleOutput { + fn pos(&mut self, x: usize, y: usize); + fn writeb(&mut self, b: u8); + fn write<T: AsRef<[u8]>>(&mut self, a: T); + fn clear(&mut self); +} + +const BUFFER_SIZE: usize = 16; + +pub struct Console { + rb: ArrayDeque<[u8; BUFFER_SIZE], arraydeque::Saturating>, +} + +impl Console { + pub fn new() -> Console { + Console { + rb: ArrayDeque::new(), + } + } + + pub fn reset(&mut self) { + interrupt::free(|_| { + self.rb.clear(); + }); + } + + pub fn pushb(&mut self, b: u8) { + interrupt::free(|_| { + self.rb.push_back(b).ok(); + }); + } +} + +impl ConsoleInput for Console { + fn readb(&mut self) -> Option<u8> { + interrupt::free(|_| { + self.rb.pop_front() + }) + } + + fn read(&mut self, buf: &mut [u8]) -> usize { + let len = if buf.len() > self.rb.len() { + self.rb.len() + } else { + buf.len() + }; + + for i in 0..len { + buf[i] = self.readb().unwrap(); + } + + len + } + + fn discard(&mut self) { + self.reset(); + } +} + +pub struct RustConsoleOperations { + pub readb: fn() -> Option<u8>, + pub read: fn(&mut [u8]) -> usize, + pub readline: fn(buf: &mut [u8]) -> usize, + pub writeb: fn(b: u8), + pub write: fn(a: &[u8]), + pub discard: fn(), + pub pos: fn(x: usize, y: usize), + pub clear: fn(), +} + +#[repr(C)] +pub struct CConsoleOperations { + pub readb: extern "C" fn() -> i32, + pub read: extern "C" fn(buf: *mut u8, len: usize) -> usize, + pub readline: extern "C" fn(buf: *mut u8, len: usize) -> usize, + pub writeb: extern "C" fn(b: u8), + pub write: extern "C" fn(a: *const u8, len: usize), + pub discard: extern "C" fn(), + pub pos: extern "C" fn(x: usize, y: usize), + pub clear: extern "C" fn(), +}
diff --git a/src/fault.rs b/src/fault.rs line changes: +168/-0 index 0000000..d223b76 --- /dev/null +++ b/src/fault.rs
@@ -0,0 +1,168 @@ +use cortex_m; +use cortex_m_rt::exception; +use cortex_m_rt::ExceptionFrame; +use crate::serial_console::get_global_console; +use crate::console::ConsoleOutput; +use crate::util::print_hex_padded; + +fn print_exception_frame(ef: &ExceptionFrame) { + let console = get_global_console(); + + console.write("PC: "); + print_hex_padded(ef.pc, 8); + console.write("\r\n"); + + console.write("LR: "); + print_hex_padded(ef.lr, 8); + console.write("\r\n"); + + console.write("XPSR: "); + print_hex_padded(ef.xpsr, 8); + console.write("\r\n"); + + console.write("R0: "); + print_hex_padded(ef.r0, 8); + console.write("\r\n"); + + console.write("R1: "); + print_hex_padded(ef.r1, 8); + console.write("\r\n"); + + console.write("R2: "); + print_hex_padded(ef.r2, 8); + console.write("\r\n"); + + console.write("R3: "); + print_hex_padded(ef.r3, 8); + console.write("\r\n"); + + console.write("IP: "); + print_hex_padded(ef.r12, 8); + console.write("\r\n"); +} + +unsafe fn print_stack(ef: &ExceptionFrame) { + let console = get_global_console(); + let sp = ef as *const ExceptionFrame as *const u32; + + console.write("SP: "); + print_hex_padded(sp as u32, 8); + console.write("\r\n"); + + for i in 0..4 { + print_hex_padded((sp as u32) + i * 32, 8); + console.write(": "); + for j in 0..8 { + print_hex_padded(*sp.offset((i * 8 + j) as isize), 8); + console.write(" "); + } + console.write("\r\n"); + } +} + +#[exception] +unsafe fn HardFault(ef: &ExceptionFrame) -> ! { + let console = get_global_console(); + console.write("\r\nHardFault!\r\n"); + print_exception_frame(ef); + console.write("\r\n"); + + print_stack(ef); + console.write("\r\n"); + + let scb = &(*cortex_m::peripheral::SCB::ptr()); + + let hfsr = scb.hfsr.read(); + console.write("HFSR: "); + print_hex_padded(hfsr, 8); + if hfsr & 0x00000002 != 0 { + console.write(" VECTBL"); + } + if hfsr & 0x40000000 != 0 { + console.write(" FORCED"); + } + console.write("\r\n"); + + if hfsr & 0x40000000 != 0 { + // read all the other fault regs + let cfsr = scb.cfsr.read(); + + let mmfsr = (cfsr & 0xFF) as u8; + console.write("MMFSR: "); + print_hex_padded(mmfsr, 8); + if mmfsr & 0x01 != 0 { + console.write(" IACCVIOL"); + } + if mmfsr & 0x02 != 0 { + console.write(" DACCVIOL"); + } + if mmfsr & 0x08 != 0 { + console.write(" MUNSTKERR"); + } + if mmfsr & 0x10 != 0 { + console.write(" MSTKERR"); + } + console.write("\r\n"); + + if mmfsr & 0x80 != 0 { + console.write("MMFAR: "); + print_hex_padded(scb.mmfar.read(), 8); + } else { + console.write("MMFAR invalid"); + } + console.write("\r\n"); + + let bfsr = ((cfsr & 0xFF00) >> 8) as u8; + console.write("BFSR: "); + print_hex_padded(bfsr, 2); + if bfsr & 0x01 != 0 { + console.write(" IBUSERR"); + } + if bfsr & 0x02 != 0 { + console.write(" PRECISERR"); + } + if bfsr & 0x04 != 0 { + console.write(" IMPRECISERR"); + } + if bfsr & 0x08 != 0 { + console.write(" UNSTKERR"); + } + if bfsr & 0x10 != 0 { + console.write(" STKERR"); + } + console.write("\r\n"); + + if bfsr & 0x80 != 0 { + console.write("BFAR: "); + print_hex_padded(scb.bfar.read(), 8); + } else { + console.write("BFAR invalid"); + } + console.write("\r\n"); + + let ufsr = ((cfsr & 0xFFFF0000) >> 16) as u16; + console.write("UFSR: "); + print_hex_padded(ufsr, 4); + if ufsr & 0x0001 != 0 { + console.write(" UNDEFINSTR"); + } + if ufsr & 0x0002 != 0 { + console.write(" INVSTATE"); + } + if ufsr & 0x0004 != 0 { + console.write(" INVPC"); + } + if ufsr & 0x0008 != 0 { + console.write(" NOCP"); + } + if ufsr & 0x0100 != 0 { + console.write(" UNALIGNED"); + } + if ufsr & 0x0200 != 0 { + console.write(" DIVBYZERO"); + } + console.write("\r\n"); + } + + loop {} +}
diff --git a/src/main.rs b/src/main.rs line changes: +109/-0 index 0000000..90229e4 --- /dev/null +++ b/src/main.rs
@@ -0,0 +1,109 @@ +#![no_std] +#![no_main] +#![feature(generators, generator_trait)] +#![feature(global_asm)] +#![feature(alloc)] +#![feature(alloc_error_handler)] +#![feature(asm)] +#![feature(const_vec_new)] +#![feature(const_fn)] + +use cortex_m_rt::{entry, pre_init}; +use embedded_hal::watchdog::WatchdogDisable; // REMOVE when embedded-hal watchdog is finalized +use alloc_cortex_m::CortexMHeap; +use r0; + +mod util; +mod console; +mod serial_console; +mod peripherals; +mod mpu; +mod task; +mod svcall; +mod alloc; +mod fault; +mod panic; +mod video; + +use console::ConsoleOutput; +use peripherals::Peripherals; +use task::Task; + +#[global_allocator] +#[link_section = ".kgbss"] +static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); + +struct Kernel { + peripherals: Peripherals, +} + +impl Kernel { + pub fn new() -> Kernel { + Kernel { + peripherals: Peripherals::new(), + } + } + + pub fn get_peripherals(&mut self) -> &mut Peripherals { + &mut self.peripherals + } +} + +extern "C" { + fn shell_main() -> u32; +} + +#[allow(dead_code)] +pub struct RustDriverInterfaces { + console: &'static console::RustConsoleOperations, + alloc: &'static alloc::RustAllocOperations, +} + +#[link_section = ".driver_interfaces"] +pub static RUST_DRIVER_INTERFACES: RustDriverInterfaces = RustDriverInterfaces { + console: &serial_console::RUST_CONSOLE_OPERATIONS, + alloc: &alloc::RUST_ALLOC_OPERATIONS, +}; + +#[allow(dead_code)] +#[repr(C)] +pub struct CDriverInterfaces { + console: &'static console::CConsoleOperations, + alloc: &'static alloc::CAllocOperations, +} + +#[link_section = ".driver_interfaces"] +pub static C_DRIVER_INTERFACES: CDriverInterfaces = CDriverInterfaces { + console: &serial_console::C_CONSOLE_OPERATIONS, + alloc: &alloc::C_ALLOC_OPERATIONS, +}; + +#[pre_init] +unsafe fn pre_init() { + extern "C" { + static mut __skgbss: u32; + static mut __ekgbss: u32; + + static mut __skgdata: u32; + static mut __ekgdata: u32; + static __sikgdata: u32; + } + + // Initialize KGRAM + r0::zero_bss(&mut __skgbss, &mut __ekgbss); + r0::init_data(&mut __skgdata, &mut __ekgdata, &__sikgdata); +} + +#[entry] +fn main() -> ! { + let mut kernel = Kernel::new(); + + let peripherals = kernel.get_peripherals(); + + peripherals.watchdog.disable(); + + serial_console::get_global_console().write("Kernel initialized!\r\n"); + + let t = Task::default_layout_full(shell_main); + t.run() +}
diff --git a/src/mpu.rs b/src/mpu.rs line changes: +132/-0 index 0000000..183e1c0 --- /dev/null +++ b/src/mpu.rs
@@ -0,0 +1,132 @@ +#![allow(dead_code)] + +use core::cell::RefCell; +use cortex_m::interrupt::{self, Mutex}; +use cortex_m::peripheral::MPU; + +#[derive(Copy, Clone, Debug)] +pub enum MemoryPermission { + None, + RO, + RW, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct MemoryRange { + pub start: u32, + pub length: u32, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct MemoryRegion { + pub start: u32, + pub length: u32, + pub permissions: MemoryPermission, + pub xn: bool, +} + +static MPU_MANAGER: Mutex<RefCell<Option<MpuManager>>> = Mutex::new(RefCell::new(None)); + +pub fn init(mpu: MPU) { + interrupt::free(|cs| MPU_MANAGER.borrow(cs).replace(Some(MpuManager::new(mpu)))); +} + +pub fn add_region(region: &MemoryRegion) { + interrupt::free(|cs| { + let mut rm = MPU_MANAGER.borrow(cs).borrow_mut(); + let manager = rm.as_mut().unwrap(); + unsafe { manager.add_region(®ion) }; + }); +} + +pub fn add_regions(regions: &[MemoryRegion]) { + interrupt::free(|cs| { + let mut rm = MPU_MANAGER.borrow(cs).borrow_mut(); + let manager = rm.as_mut().unwrap(); + for region in regions { + unsafe { manager.add_region(®ion) }; + } + }); +} + +pub fn enable() { + interrupt::free(|cs| { + let mut rm = MPU_MANAGER.borrow(cs).borrow_mut(); + let manager = rm.as_mut().unwrap(); + manager.set_mpu_enable(true); + }); +} + +pub fn disable() { + interrupt::free(|cs| { + let mut rm = MPU_MANAGER.borrow(cs).borrow_mut(); + let manager = rm.as_mut().unwrap(); + manager.set_mpu_enable(false); + }); +} + +struct MpuManager { + mpu: MPU, + n_mpu_regions: u32, +} + +impl MpuManager { + pub fn new(mpu: MPU) -> MpuManager { + // Enable default memory map for privileged software + unsafe { mpu.ctrl.write(0x4) }; + + MpuManager { + mpu, + n_mpu_regions: 0, + } + } + + unsafe fn write_mpu_region(&mut self, start: u32, length: u32, region: &MemoryRegion) { + if self.n_mpu_regions == 8 { + panic!("Out of MPU regions"); + } + + // We don't check the start value here. Let the fault handler handle it. + self.mpu.rbar.write(start | 0b10000 | self.n_mpu_regions); + self.mpu.rasr.write( + if region.xn { 1 << 28 } else { 0 } | // XN + match region.permissions { + MemoryPermission::None => 0b001, + MemoryPermission::RO => 0b010, + MemoryPermission::RW => 0b011, + } << 24 | // AP + 0b001000 << 16 | // TEX S C B + (length.trailing_zeros() - 1) << 1 | // SIZE + 1 // ENABLE + ); + self.n_mpu_regions += 1; + } + + unsafe fn map_mpu_region(&mut self, start: u32, length: u32, region: &MemoryRegion) { + if length.count_ones() > 1 { + // The region is not a power of two. Create a MPU region for + // the next lowest power of two and then iterate on the + // remainder. + let nl_length = length.next_power_of_two() >> 1; + self.write_mpu_region(start, nl_length, region); + self.map_mpu_region(start + nl_length, length - nl_length, region); + } else { + self.write_mpu_region(start, length, region); + } + } + + pub unsafe fn add_region(&mut self, region: &MemoryRegion) { + if region.length == 0 { + return; + } + self.map_mpu_region(region.start, region.length, region); + } + + pub fn set_mpu_enable(&mut self, enable: bool) { + unsafe { + self.mpu.ctrl.modify(|v| (v & 0xFFFFFFFE) | (enable as u32)) + }; + } +}
diff --git a/src/panic.rs b/src/panic.rs line changes: +32/-0 index 0000000..412d7e0 --- /dev/null +++ b/src/panic.rs
@@ -0,0 +1,32 @@ +use core::panic::PanicInfo; +use crate::serial_console::get_global_console; +use crate::console::ConsoleOutput; +use crate::util::print_dec; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + // Boy if we panic before the console is up, we're just SOL. + let console = get_global_console(); + + console.write("PANIC!\r\n"); + + match info.payload().downcast_ref::<&str>() { + Some(p) => console.write(p), + None => console.write("no payload"), + }; + console.write("\r\n"); + + if let Some(location) = info.location() { + console.write("at "); + console.write(location.file()); + console.write(" line "); + print_dec(location.line()); + console.write(":"); + print_dec(location.column()); + console.write("\r\n"); + } else { + console.write("Unknown location\r\n"); + } + + loop{} +}
diff --git a/src/peripherals.rs b/src/peripherals.rs line changes: +82/-0 index 0000000..4d15f99 --- /dev/null +++ b/src/peripherals.rs
@@ -0,0 +1,82 @@ +use cortex_m::peripheral::MPU; +use atsam3xa_hal::prelude::*; +use crate::serial_console::SerialConsole; +use crate::mpu::{self, MemoryRegion, MemoryPermission}; +use crate::video; + +pub struct Peripherals { + pub led: atsam3xa_hal::pio::Pin, + pub watchdog: atsam3xa_hal::watchdog::WDT, +} + +impl Peripherals { + pub fn new() -> Peripherals { + let mut mp = cortex_m::peripheral::Peripherals::take().unwrap(); + let pp = atsam3xa::Peripherals::take().unwrap(); + + // Initialize flash wait states + pp.EFC0.constrain().set_wait_states(4); + pp.EFC1.constrain().set_wait_states(4); + + // Initialize clocks + let pmc = pp.PMC.constrain(); + pmc.init_clocks(); + + pmc.enable_peripheral_clock(PeripheralID::PIOB); + pmc.enable_peripheral_clock(PeripheralID::UART); + + let pio_a = pp.PIOA.constrain(); + let pio_b = pp.PIOB.constrain(); + + let led = pio_b.get_pin(27); + led.output_mode(); + + let uart = pp.UART.constrain(&pio_a, 115200, ParityType::NO); + mp.NVIC.enable(atsam3xa::Interrupt::UART); + unsafe { mp.NVIC.set_priority(atsam3xa::Interrupt::UART, 7 << 4) }; + uart.enable_interrupts(); + SerialConsole::new(uart); + + let watchdog = pp.WDT.constrain(); + + Self::mpu_setup(mp.MPU); + + video::gen::start(pp.TC0, &mut mp.NVIC, &pmc, &pio_b); + + Peripherals { + led, + watchdog, + } + } + + pub fn mpu_setup(mpu: MPU) { + mpu::init(mpu); + + mpu::add_regions(&[ + // ROM + MemoryRegion { + start: 0x00000000, + length: 0x20000000, + permissions: MemoryPermission::RO, + xn: false, + }, + // Peripherals + MemoryRegion { + start: 0x40000000, + length: 0x20000000, + permissions: MemoryPermission::RW, + xn: true, + }, + // KGRAM + MemoryRegion { + start: 0x20080000, + length: 0x4000, + permissions: MemoryPermission::RW, + xn: true, + }, + ]); + + // Enable MPU + mpu::enable(); + } +}
diff --git a/src/serial_console.rs b/src/serial_console.rs line changes: +211/-0 index 0000000..54b2aff --- /dev/null +++ b/src/serial_console.rs
@@ -0,0 +1,211 @@ +use crate::console::*; + +use core::slice; +use nb::block; +use embedded_hal::serial::*; +use atsam3xa::interrupt; + +#[link_section = ".kgbss"] +static mut G_CONSOLE: Option<SerialConsole> = None; + +pub struct SerialConsole { + console: Console, + uart: atsam3xa_hal::uart::UART, +} + +impl SerialConsole { + pub fn new(uart: atsam3xa_hal::uart::UART) { + unsafe { + G_CONSOLE = Some(SerialConsole { + console: Console::new(), + uart: uart, + }); + } + } + + pub fn readline(&mut self, buf: &mut [u8]) -> usize { + let mut c = 0; + + loop { + let byte = match self.readb() { + Some(b) => b, + None => continue, + }; + + if byte == 0xd { + break; + } else if byte == 0x8 { + if c == 0 { + // buffer empty + continue; + } + self.write("\x08 \x08"); + c -= 1; + continue; + } + + if c == buf.len() { + // Don't overflow the buffer + continue; + } + + self.writeb(byte); + buf[c] = byte; + c += 1; + } + + c + } + + fn handle_interrupt(&mut self) { + if let Ok(b) = self.uart.read() { + self.console.pushb(b); + } + } +} + +impl ConsoleOutput for SerialConsole { + fn pos(&mut self, x: usize, y: usize) { + } + + fn writeb(&mut self, b: u8) { + block!(self.uart.write(b)).ok(); + } + + fn write<T: AsRef<[u8]>>(&mut self, a: T) { + let bytes = a.as_ref(); + for b in bytes { + self.writeb(*b); + } + } + + fn clear(&mut self) { + self.write(&[0x1b, b'[', b'2', b'J', 0x1b, b'[', b'H']); + } +} + +impl ConsoleInput for SerialConsole { + fn readb(&mut self) -> Option<u8> { + self.console.readb() + } + + fn read(&mut self, buf: &mut [u8]) -> usize { + self.console.read(buf) + } + + fn discard(&mut self) { + self.console.discard() + } +} + +pub fn get_global_console() -> &'static mut SerialConsole { + unsafe { + core::intrinsics::transmute(&mut G_CONSOLE) + } +} + +#[interrupt] +fn UART() { + get_global_console().handle_interrupt(); +} + + +/* Console driver function table */ + +/* Rust versions */ +fn rust_readb() -> Option<u8> { + get_global_console().readb() +} + +fn rust_read(buf: &mut [u8]) -> usize { + get_global_console().read(buf) +} + +fn rust_readline(buf: &mut [u8]) -> usize { + get_global_console().readline(buf) +} + +fn rust_writeb(b: u8) { + get_global_console().writeb(b) +} + +fn rust_write(a: &[u8]) { + get_global_console().write(a) +} + +fn rust_discard() { + get_global_console().discard() +} + +fn rust_pos(x: usize, y: usize) { + get_global_console().pos(x, y) +} + +fn rust_clear() { + get_global_console().clear() +} + +pub const RUST_CONSOLE_OPERATIONS: RustConsoleOperations = RustConsoleOperations { + readb: rust_readb, + read: rust_read, + readline: rust_readline, + writeb: rust_writeb, + write: rust_write, + discard: rust_discard, + pos: rust_pos, + clear: rust_clear, +}; + +/* C versions */ +extern "C" fn c_readb() -> i32 { + match rust_readb() { + Some(b) => b as i32, + None => -1, + } +} + +extern "C" fn c_read(buf: *mut u8, len: usize) -> usize { + if len == 0 { + return 0; + } + + let buf = unsafe { slice::from_raw_parts_mut(buf, len) }; + rust_read(buf) +} + +extern "C" fn c_readline(buf: *mut u8, len: usize) -> usize { + let buf = unsafe { slice::from_raw_parts_mut(buf, len) }; + rust_readline(buf) +} + +extern "C" fn c_writeb(b: u8) { + rust_writeb(b) +} + +extern "C" fn c_write(a: *const u8, len: usize) { + let a = unsafe { slice::from_raw_parts(a, len) }; + rust_write(a) +} + +extern "C" fn c_discard() { + rust_discard() +} + +extern "C" fn c_pos(x: usize, y: usize) { + rust_pos(x, y) +} + +extern "C" fn c_clear() { + rust_clear() +} + +pub const C_CONSOLE_OPERATIONS: CConsoleOperations = CConsoleOperations { + readb: c_readb, + read: c_read, + readline: c_readline, + writeb: c_writeb, + write: c_write, + discard: c_discard, + pos: c_pos, + clear: c_clear, +};
diff --git a/src/svcall.rs b/src/svcall.rs line changes: +46/-0 index 0000000..b1de777 --- /dev/null +++ b/src/svcall.rs
@@ -0,0 +1,46 @@ +use cortex_m_rt::ExceptionFrame; +use crate::console::ConsoleOutput; +use crate::serial_console::get_global_console; +use crate::util::{print_dec, print_hex_padded}; + +global_asm!(r#" + .global SVCall + .type SVCall,function +SVCall: + // Store a pointer to registers in r1 + mrs r1, PSP + // find the svc number from the app stack, store it in r0 + ldr r0, [r1, #24] + ldr r0, [r0, #-2] + and r0, 0xFF + // call service handler + b svcall_handler +"#); + +#[no_mangle] +unsafe extern "C" fn svcall_handler(svc: u8, regs: &mut ExceptionFrame) { + match svc { + 0 => { + regs.r0 = &crate::C_DRIVER_INTERFACES as *const crate::CDriverInterfaces as u32 + }, + 1 => { + regs.r0 = &crate::RUST_DRIVER_INTERFACES as *const crate::RustDriverInterfaces as u32 + }, + _ => { + let console = get_global_console(); + console.write("unknown svcall "); + print_dec(svc); + console.write(": "); + + print_hex_padded(regs.r0, 8); + console.write(" "); + print_hex_padded(regs.r1, 8); + console.write(" "); + print_hex_padded(regs.r2, 8); + console.write(" "); + print_hex_padded(regs.r3, 8); + + console.write("\r\n"); + }, + } +}
diff --git a/src/task.rs b/src/task.rs line changes: +90/-0 index 0000000..0441870 --- /dev/null +++ b/src/task.rs
@@ -0,0 +1,90 @@ +use core::ptr; +use crate::mpu::{self, MemoryRegion, MemoryPermission, MemoryRange}; + +extern "C" { + static _appram: u32; + static _appram_end: u32; + pub fn launch(entry: u32, sp: u32, data_base: u32) -> !; +} + +global_asm!(r#" + .global launch + .type launch,function +launch: + // Move entry address out of the way + mov r3, r0 + // set up PSP and drop privileges + mov r0, 0x3 + msr CONTROL, r0 + // set stack pointer and pointer to data region info + mov sp, r1 + mov r0, r1 + // set up memory region pointer + mov r9, r2 + isb + dsb + bx r3 +"#); + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct Task { + text: MemoryRegion, + data: MemoryRegion, + stack: MemoryRegion, + + pc: u32, + sp: u32, +} + +impl Task { + pub fn default_layout_full(entry: unsafe extern "C" fn() -> u32) -> Task { + let appram_base = unsafe { &_appram as *const u32 as u32 }; + let stack_start = appram_base + 0x10000 + 0xe000; + let stack_size = 0x2000; + let stack_end = stack_start + stack_size; + + Task { + text: MemoryRegion { + start: 0x00000000, + length: 0, + permissions: MemoryPermission::None, + xn: false, + }, + data: MemoryRegion { + start: appram_base, + length: 0x10000, + permissions: MemoryPermission::RW, + xn: true, + }, + stack: MemoryRegion { + start: stack_start, + length: stack_size, + permissions: MemoryPermission::RW, + xn: true, + }, + + pc: entry as *const () as u32, + sp: stack_end, + } + } + + pub fn run(&self) -> ! { + mpu::add_region(&self.text); + mpu::add_region(&self.data); + mpu::add_region(&self.stack); + + unsafe { + let sp = self.sp as *mut MemoryRange; + let sp = sp.sub(1); + ptr::write(sp, MemoryRange { + start: self.data.start, + length: self.data.length - self.stack.length, + }); + let sp = sp as u32; + + launch(self.pc, sp, self.data.start) + } + } + +}
diff --git a/src/util.rs b/src/util.rs line changes: +120/-0 index 0000000..8af31da --- /dev/null +++ b/src/util.rs
@@ -0,0 +1,120 @@ +#![allow(dead_code)] +use crate::serial_console::get_global_console; +use crate::console::ConsoleOutput; + +pub fn dec_digit(n: u32) -> u8 { + b'0' + (n % 10) as u8 +} + +pub fn hex_digit(n: u32) -> u8 { + let mut n = (n & 0xF) as u8; + + if n > 9 { + n += 7; + } + + b'0' + n +} + +#[test] +fn test_hex_digits() { + assert!(hex_digit(0) == b'0'); + assert!(hex_digit(10) == b'A'); +} + +pub fn format_int_dec(n: u32, buf: &mut [u8]) -> usize { + if n == 0 { + buf[buf.len() - 1] = b'0'; + return 1; + } + + let mut n = n; + let mut c = buf.len() - 1; + loop { + buf[c] = dec_digit(n); + n /= 10; + if c == 0 || n == 0 { + break; + } + c -= 1; + } + + buf.len() - c +} + +#[test] +fn test_format_int_dec() { + let mut buf = [0u8; 9]; + + let n = format_int_dec(0, &mut buf); + assert!(n == 1); + assert!(buf[8] == b'0'); + + let n = format_int_dec(255, &mut buf); + assert!(n == 3); + assert!(buf[6..] == [b'2', b'5', b'5']); + + let n = format_int_dec(0x20000000, &mut buf); + assert!(n == 9); + assert!(buf == "536870912".as_bytes()); +} + +pub fn format_int_hex(n: u32, buf: &mut[u8]) -> usize { + if n == 0 { + buf[buf.len() - 1] = b'0'; + return 1; + } + + let mut n = n; + let mut c = buf.len() - 1; + loop { + buf[c] = hex_digit(n); + n >>= 4; + if c == 0 || n == 0 { + break; + } + c -= 1; + } + + buf.len() - c +} + +#[test] +fn test_format_int_hex() { + let mut buf = [0u8; 8]; + + let n = format_int_hex(0, &mut buf); + assert!(n == 1); + assert!(buf[7] == b'0'); + + let n = format_int_hex(0x20000000, &mut buf); + assert!(n == 8); + assert!(buf == "20000000".as_bytes()); +} + +pub fn print_hex<T: Into<u32>>(v: T) { + let v: u32 = v.into(); + let console = get_global_console(); + let mut buf = [0u8; 8]; + + let n = format_int_hex(v, &mut buf); + console.write(&buf[8 - n..]); +} + +pub fn print_hex_padded<T: Into<u32>>(v: T, digits: usize) { + let v: u32 = v.into(); + let console = get_global_console(); + let mut buf = [b'0'; 8]; + + format_int_hex(v, &mut buf); + console.write(&buf[8 - digits..]); +} + +pub fn print_dec<T: Into<u32>>(v: T) { + let v: u32 = v.into(); + let console = get_global_console(); + let mut buf = [0u8; 10]; + + let n = format_int_dec(v, &mut buf); + console.write(&buf[10 - n..]); +}
diff --git a/src/video/font.rs b/src/video/font.rs line changes: +2818/-0 index 0000000..030baab --- /dev/null +++ b/src/video/font.rs
@@ -0,0 +1,2818 @@ +pub static FONT: [[u8; 8]; 256] = [ + [ + 0b10101010, + 0b01010101, + 0b10101010, + 0b01010101, + 0b10101010, + 0b01010101, + 0b10101010, + 0b01010101, + + ], + [ + 0b01010101, + 0b10101010, + 0b01010101, + 0b10101010, + 0b01010101, + 0b10101010, + 0b01010101, + 0b10101010, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00010000, + 0b00111000, + 0b00111000, + 0b00010000, + 0b00010000, + 0b00000000, + 0b00010000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00101000, + 0b00101000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00100100, + 0b01111110, + 0b00100100, + 0b00100100, + 0b01111110, + 0b00100100, + 0b00000000, + + ], + [ + 0b00010000, + 0b00111000, + 0b01010000, + 0b00111000, + 0b00010100, + 0b00111000, + 0b00010000, + 0b00000000, + + ], + [ + 0b01000010, + 0b10100100, + 0b01001000, + 0b00010000, + 0b00100100, + 0b01001010, + 0b10000100, + 0b00000000, + + ], + [ + 0b00010000, + 0b00101000, + 0b00010000, + 0b00101000, + 0b01001010, + 0b01000100, + 0b00111010, + 0b00000000, + + ], + [ + 0b00000000, + 0b00010000, + 0b00010000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00001000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00001000, + 0b00000000, + + ], + [ + 0b00010000, + 0b00001000, + 0b00001000, + 0b00001000, + 0b00001000, + 0b00001000, + 0b00010000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00010000, + 0b01010100, + 0b00111000, + 0b01010100, + 0b00010000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00010000, + 0b00010000, + 0b01111100, + 0b00010000, + 0b00010000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00110000, + 0b00110000, + 0b00010000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01111100, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00110000, + 0b00110000, + 0b00000000, + + ], + [ + 0b00000100, + 0b00001000, + 0b00001000, + 0b00010000, + 0b00010000, + 0b00100000, + 0b00100000, + 0b01000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b01001100, + 0b01010100, + 0b01100100, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b00010000, + 0b00110000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00111000, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b00000100, + 0b00001000, + 0b00010000, + 0b00100000, + 0b01111100, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b00000100, + 0b00011000, + 0b00000100, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b00011000, + 0b00101000, + 0b01001000, + 0b10001000, + 0b11111100, + 0b00001000, + 0b00001000, + 0b00000000, + + ], + [ + 0b01111100, + 0b01000000, + 0b01000000, + 0b01111000, + 0b00000100, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b01000000, + 0b01111000, + 0b01000100, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b01111100, + 0b00000100, + 0b00001000, + 0b00001000, + 0b00010000, + 0b00010000, + 0b00100000, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b01000100, + 0b00111000, + 0b01000100, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b01000100, + 0b00111100, + 0b00000100, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00110000, + 0b00110000, + 0b00000000, + 0b00000000, + 0b00110000, + 0b00110000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00110000, + 0b00110000, + 0b00000000, + 0b00000000, + 0b00110000, + 0b00110000, + 0b00010000, + + ], + [ + 0b00000100, + 0b00001000, + 0b00010000, + 0b00100000, + 0b00010000, + 0b00001000, + 0b00000100, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b01111100, + 0b00000000, + 0b00000000, + 0b01111100, + 0b00000000, + 0b00000000, + + ], + [ + 0b01000000, + 0b00100000, + 0b00010000, + 0b00001000, + 0b00010000, + 0b00100000, + 0b01000000, + 0b00000000, + + ], + [ + 0b00010000, + 0b00101000, + 0b00001000, + 0b00010000, + 0b00010000, + 0b00000000, + 0b00010000, + 0b00000000, + + ], + [ + 0b00000000, + 0b01111000, + 0b10000100, + 0b10110100, + 0b10110100, + 0b10001100, + 0b01100000, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b01000100, + 0b01000100, + 0b01111100, + 0b01000100, + 0b01000100, + 0b00000000, + + ], + [ + 0b01111000, + 0b01000100, + 0b01000100, + 0b01111000, + 0b01000100, + 0b01000100, + 0b01111000, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b01000000, + 0b01000000, + 0b01000000, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b01111000, + 0b01000100, + 0b01000100, + 0b01000100, + 0b01000100, + 0b01000100, + 0b01111000, + 0b00000000, + + ], + [ + 0b01111100, + 0b01000000, + 0b01000000, + 0b01110000, + 0b01000000, + 0b01000000, + 0b01111100, + 0b00000000, + + ], + [ + 0b01111100, + 0b01000000, + 0b01000000, + 0b01110000, + 0b01000000, + 0b01000000, + 0b01000000, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b01000000, + 0b01000000, + 0b01001100, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b01000100, + 0b01000100, + 0b01000100, + 0b01111100, + 0b01000100, + 0b01000100, + 0b01000100, + 0b00000000, + + ], + [ + 0b01111100, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b01111100, + 0b00000000, + + ], + [ + 0b00011100, + 0b00001000, + 0b00001000, + 0b00001000, + 0b00001000, + 0b01001000, + 0b00110000, + 0b00000000, + + ], + [ + 0b01000100, + 0b01001000, + 0b01010000, + 0b01100000, + 0b01010000, + 0b01001000, + 0b01000100, + 0b00000000, + + ], + [ + 0b01000000, + 0b01000000, + 0b01000000, + 0b01000000, + 0b01000000, + 0b01000000, + 0b01111100, + 0b00000000, + + ], + [ + 0b01000100, + 0b01101100, + 0b01010100, + 0b01010100, + 0b01010100, + 0b01010100, + 0b01010100, + 0b00000000, + + ], + [ + 0b01000100, + 0b01100100, + 0b01010100, + 0b01001100, + 0b01000100, + 0b01000100, + 0b01000100, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b01000100, + 0b01000100, + 0b01000100, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b01111000, + 0b01000100, + 0b01000100, + 0b01111000, + 0b01000000, + 0b01000000, + 0b01000000, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b01000100, + 0b01000100, + 0b01010100, + 0b01001100, + 0b00111110, + 0b00000000, + + ], + [ + 0b01111000, + 0b01000100, + 0b01000100, + 0b01111000, + 0b01010000, + 0b01001000, + 0b01000100, + 0b00000000, + + ], + [ + 0b00111000, + 0b01000100, + 0b01000000, + 0b00111000, + 0b00000100, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b01111100, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00000000, + + ], + [ + 0b01000100, + 0b01000100, + 0b01000100, + 0b01000100, + 0b01000100, + 0b01000100, + 0b00111000, + 0b00000000, + + ], + [ + 0b01000100, + 0b01000100, + 0b01000100, + 0b01000100, + 0b00101000, + 0b00101000, + 0b00010000, + 0b00000000, + + ], + [ + 0b10000010, + 0b10000010, + 0b10010010, + 0b01010100, + 0b01010100, + 0b01010100, + 0b00101000, + 0b00000000, + + ], + [ + 0b01000100, + 0b01000100, + 0b00101000, + 0b00010000, + 0b00101000, + 0b01000100, + 0b01000100, + 0b00000000, + + ], + [ + 0b01000100, + 0b01000100, + 0b00101000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00000000, + + ], + [ + 0b01111100, + 0b00000100, + 0b00001000, + 0b00010000, + 0b00100000, + 0b01000000, + 0b01111100, + 0b00000000, + + ], + [ + 0b00011100, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00011100, + 0b00000000, + + ], + [ + 0b01000000, + 0b01000000, + 0b00100000, + 0b00100000, + 0b00010000, + 0b00010000, + 0b00001000, + 0b00001000, + + ], + [ + 0b01110000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b01110000, + 0b00000000, + + ], + [ + 0b00010000, + 0b00101000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b01111100, + 0b00000000, + + ], + [ + 0b00000000, + 0b01000000, + 0b00100000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00111000, + 0b01001000, + 0b01001000, + 0b00110100, + 0b00000000, + + ], + [ + 0b00000000, + 0b01000000, + 0b01000000, + 0b01110000, + 0b01001000, + 0b01001000, + 0b01110000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00110000, + 0b01000000, + 0b01000000, + 0b00110000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00001000, + 0b00001000, + 0b00111000, + 0b01001000, + 0b01001000, + 0b00111000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00110000, + 0b01111000, + 0b01000000, + 0b00110000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00010000, + 0b00100000, + 0b00100000, + 0b01110000, + 0b00100000, + 0b00100000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00110000, + 0b01001000, + 0b01001000, + 0b00111000, + 0b00001000, + 0b00110000, + + ], + [ + 0b00000000, + 0b01000000, + 0b01000000, + 0b01110000, + 0b01001000, + 0b01001000, + 0b01001000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00100000, + 0b00000000, + 0b00100000, + 0b00100000, + 0b00100000, + 0b00100000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00010000, + 0b00000000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b01010000, + 0b00100000, + + ], + [ + 0b00000000, + 0b01000000, + 0b01000000, + 0b01010000, + 0b01100000, + 0b01100000, + 0b01010000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00100000, + 0b00100000, + 0b00100000, + 0b00100000, + 0b00100000, + 0b00100000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01101000, + 0b01010100, + 0b01010100, + 0b01010100, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01110000, + 0b01001000, + 0b01001000, + 0b01001000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00110000, + 0b01001000, + 0b01001000, + 0b00110000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01110000, + 0b01001000, + 0b01001000, + 0b01110000, + 0b01000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00110000, + 0b01001000, + 0b01001000, + 0b00111000, + 0b00001100, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01010000, + 0b01101000, + 0b01000000, + 0b01000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01110000, + 0b01000000, + 0b00010000, + 0b01110000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00100000, + 0b01110000, + 0b00100000, + 0b00100000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01001000, + 0b01001000, + 0b01001000, + 0b00110000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01010000, + 0b01010000, + 0b01010000, + 0b00100000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01000100, + 0b01000100, + 0b01010100, + 0b00101000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01001000, + 0b00110000, + 0b00110000, + 0b01001000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01001000, + 0b01001000, + 0b00111000, + 0b00001000, + 0b00110000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b01111000, + 0b00010000, + 0b00100000, + 0b01111000, + 0b00000000, + + ], + [ + 0b00000100, + 0b00001000, + 0b00001000, + 0b00010000, + 0b00001000, + 0b00001000, + 0b00000100, + 0b00000000, + + ], + [ + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00000000, + + ], + [ + 0b01000000, + 0b00100000, + 0b00100000, + 0b00010000, + 0b00100000, + 0b00100000, + 0b01000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00100000, + 0b01010100, + 0b00001000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00011111, + 0b00100001, + 0b01010101, + 0b10001001, + 0b01010101, + 0b00100001, + 0b00011111, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], + [ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + + ], +];
diff --git a/src/video/gen.rs b/src/video/gen.rs line changes: +343/-0 index 0000000..4318751 --- /dev/null +++ b/src/video/gen.rs
@@ -0,0 +1,343 @@ +use cortex_m::asm; +use cortex_m::peripheral::NVIC; +use atsam3xa::{TC0}; +use atsam3xa::interrupt; +use embedded_hal::digital::OutputPin; +use atsam3xa_hal::prelude::*; +use atsam3xa_hal::pmc::PMC; +use atsam3xa_hal::pio::{self, PIOB}; + +use crate::video::mem::{ROWS, COLS, VIDEO_RAM}; +use crate::video::font::FONT; + +#[allow(dead_code)] +enum VideoStage { + EqualizingHead, + FieldSync, + Equalizing, + BlankLine, + Line, + HalfLineTop, + HalfLineBottom, +} + +struct GenOp { + stage: VideoStage, + count: usize, + call_every: usize, +} + +const OPERATIONS: [GenOp; 5] = [ + // EqualizingHead sets TC period to half a frame for the vblank period + GenOp { stage: VideoStage::EqualizingHead, count: 6, call_every: 6 }, + GenOp { stage: VideoStage::FieldSync, count: 6, call_every: 6 }, + GenOp { stage: VideoStage::Equalizing, count: 6, call_every: 6 }, + // BlankLine sets TC period back to a full frame + GenOp { stage: VideoStage::BlankLine, count: 11, call_every: 11 }, + // 242 visible lines(?) + GenOp { stage: VideoStage::Line, count: 242, call_every: 1 }, + /* + // HalfLineTop sets TC period back to half a frame before entering vblank on second field + GenOp { stage: VideoStage::HalfLineTop, count: 1, call_every: 1 }, + GenOp { stage: VideoStage::Equalizing, count: 6, call_every: 6 }, + GenOp { stage: VideoStage::FieldSync, count: 6, call_every: 6 }, + GenOp { stage: VideoStage::Equalizing, count: 5, call_every: 5 }, + // HalfLineBottom is the last equalizing cycle, but sets the period back to a full frame + GenOp { stage: VideoStage::HalfLineBottom, count: 1, call_every: 1 }, + // Another set of blank lines (this also resets the cycle, but it doesn't matter) + GenOp { stage: VideoStage::BlankLine, count: 11, call_every: 11 }, + // And 242 lines on the bottom field + GenOp { stage: VideoStage::Line, count: 232, call_every: 1 }, + GenOp { stage: VideoStage::BlankLine, count: 9, call_every: 10 }, + */ +]; + +const fn us_to_ticks(us: f64) -> u32 { + (TICKS_PER_SECOND as f64 * (us * 1e-6)) as u32 +} + +const TICKS_PER_SECOND: u32 = 42_000_000; +const TICKS_PER_FRAME: u32 = TICKS_PER_SECOND / 30; +const TICKS_PER_LINE: u32 = TICKS_PER_FRAME / 525; +const TICKS_PER_HALF_LINE: u32 = TICKS_PER_LINE / 2; +const PRE_EQ_PULSE: u32 = us_to_ticks(2.3); +const SYNC_PULSE: u32 = us_to_ticks(4.7); +const FIELD_SYNC_PULSE: u32 = TICKS_PER_HALF_LINE - SYNC_PULSE; +const BLANK_TIME: u32 = us_to_ticks(9.2); + +const LEFT_MARGIN: u32 = us_to_ticks(3.1); + +const HRES: usize = 320; +const VRES: usize = 240; +const VSCALE: usize = (240 / VRES); + +struct VideoGenerator { + tc: TC0, + // luma isn't used directly because the pixel loop is assembly + #[allow(dead_code)] + luma: pio::Pin, +} + +extern "C" { + fn pixel_pusher(mem: *const u8); + fn write_scanline(linedata: *const u8, char_mem: *const u8, font: *const u8); +} + +global_asm!(r#" + .type pixel_pusher,function +pixel_pusher: + // ARGUMENTS + // r0 = pointer to scanline data + // WORK REGISTERS + // r1 = PIOB.26 ODSR bit band address (0x43c20768) + // r2 = end address + // r3 = work byte + mov r1, 0x0768 + movt r1, 0x43c2 + add r2, r0, #40 +0: + ldr r3, [r0], #4 + +.rept 31 + ror r3, r3, 31 + str r3, [r1] + nop + nop + nop + nop + nop + nop + nop + nop +.endr + ror r3, r3, 31 + str r3, [r1] + + cmp r0, r2 + bne 0b + + // Set output to low at end of line + mov r3, #0 + str r3, [r1] + + bx lr + + .type write_scanline,function +write_scanline: + // ARGUMENTS + // r0 = scanline data address + // r1 = character memory address + // r2 = font memory address (modulo pixel row) + // WORK REGISTERS + // r3 = byte counter + // r4 = 4x character + // r5 = output word + // r6 = temporary char + push {r4-r6} + mov r3, 36 +0: + mov r5, 0 + ldr r4, [r1, r3] + + ubfx r6, r4, #0, #8 + ldrb r6, [r2, r6, lsl #3] + orr r5, r5, r6, lsl #24 + + ubfx r6, r4, #8, #8 + ldrb r6, [r2, r6, lsl #3] + orr r5, r5, r6, lsl #16 + + ubfx r6, r4, #16, #8 + ldrb r6, [r2, r6, lsl #3] + orr r5, r5, r6, lsl #8 + + ubfx r6, r4, #24, #8 + ldrb r6, [r2, r6, lsl #3] + orr r5, r5, r6 + + str r5, [r0, r3] + cbz r3, 1f + sub r3, r3, 4 + b 0b + +1: + // These nops stabilize the pixel timing somehow. May require adjustment + // if the above changes. + nop + nop + pop {r4-r6} + bx lr +"#); + +impl VideoGenerator { + fn new(tc: TC0, luma: pio::Pin) -> VideoGenerator { + let mut c: u8 = 0; + for y in 0..ROWS { + for x in 0..COLS { + unsafe { VIDEO_RAM.set_cell(x, y, c) }; + c = c + 1; + } + } + + VideoGenerator { + tc, + luma, + } + } + + fn execute(&mut self, stage: &VideoStage, count: usize) { + match stage { + VideoStage::EqualizingHead => { + self.pre_eq(); + self.half_line_period(); + }, + VideoStage::Equalizing => { + self.pre_eq(); + }, + VideoStage::FieldSync => { + self.field_sync(); + }, + VideoStage::BlankLine => { + self.line_sync(); + self.full_line_period(); + }, + VideoStage::Line => { + if count >= VRES * VSCALE { + return; + } + + let pixel_line = count / VSCALE; + let char_row = pixel_line / 8; + let char_line = pixel_line % 8; + let mut linedata : [u8; COLS] = unsafe { core::mem::uninitialized() }; + + unsafe { + let char_mem = VIDEO_RAM.row_address(char_row); + // We add the char_line offset here since it's constant for every character + let font_mem = (&FONT[0] as *const u8).offset(char_line as isize); + write_scanline(&linedata as *const u8, char_mem, font_mem); + } + + /* + let tf = self.tc.cv0.read().cv().bits(); + if count == 0 { + use crate::console::ConsoleOutput; + crate::util::print_dec(tf as u32); + crate::serial_console::get_global_console().write("\n"); + } + */ + + // wait for the sync pulse to finish + while self.tc.cv0.read().cv().bits() < BLANK_TIME + LEFT_MARGIN {} + + unsafe { + pixel_pusher(&linedata as *const u8); + } + }, + VideoStage::HalfLineTop => { + self.half_line_period(); + }, + VideoStage::HalfLineBottom => { + self.full_line_period(); + } + }; + } + + fn full_line_period(&self) { + self.tc.rc0.write(|w| unsafe { w.bits(TICKS_PER_LINE) }); + } + + fn half_line_period(&self) { + self.tc.rc0.write(|w| unsafe { w.bits(TICKS_PER_HALF_LINE) }); + } + + fn pre_eq(&self) { + // PRE_EQ_PULSE cycles low, then high + self.tc.ra0.write(|w| unsafe { w.bits(PRE_EQ_PULSE) }); + } + + fn field_sync(&self) { + // FIELD_SYNC_PULSE cycles low, then high + self.tc.ra0.write(|w| unsafe { w.bits(FIELD_SYNC_PULSE) }); + } + + fn line_sync(&self) { + // SYNC_PULSE cycles low, then high + self.tc.ra0.write(|w| unsafe { w.bits(SYNC_PULSE) }); + } +} + +static mut VIDEO_GENERATOR: Option<VideoGenerator> = None; + +#[interrupt] +fn TC0() { + static mut OP: usize = 0; + static mut COUNT: usize = 0; + + let vg = unsafe { VIDEO_GENERATOR.as_mut().unwrap() }; + // The status register is irrelevant to us, but we must read it to clear + // the interrupt. + vg.tc.sr0.read().bits(); + + let o = &OPERATIONS[*OP]; + if *COUNT % o.call_every == 0 { + vg.execute(&o.stage, *COUNT); + } + + *COUNT += 1; + if *COUNT == o.count { + *OP = (*OP + 1) % OPERATIONS.len(); + *COUNT = 0; + } +} + +pub fn start(tc: TC0, nvic: &mut NVIC, pmc: &PMC, pio_b: &PIOB) { + // Enable peripheral clock + pmc.enable_peripheral_clock(PeripheralID::TC0); + + // Set NVIC priority for TC0 interrupt + unsafe { nvic.set_priority(atsam3xa::Interrupt::TC0, 0) }; + + // Enable TC0 interrupt + nvic.enable(atsam3xa::Interrupt::TC0); + + // Configure SYNC output pin on TIOA0 + let tioa0 = pio_b.get_pin(25); + tioa0.peripheral_mode(PeripheralMultiplex::B); + + // set wave mode, up RC mode, and use timer clock 1 (MCLK/2) + unsafe { + tc.cmr0.cmr0_wave_eq_1.write(|w| { + w.wave().set_bit() + .wavsel().up_rc() + .acpa().set() + .acpc().clear() + .tcclks().timer_clock1() + }); + } + // set RC value (wait one line period before starting for no particular reason) + tc.rc0.write(|w| unsafe { w.rc().bits(TICKS_PER_LINE) }); + // enable interrupt on RC compare + tc.ier0.write(|w| w.cpcs().set_bit()); + + // Get the digital pin for B/W output + let mut luma = pio_b.get_pin(26); + luma.output_mode(); + luma.set_synchronous_mode(true); + luma.set_low(); + + unsafe { + // Store the global VideoGenerator object + VIDEO_GENERATOR = Some(VideoGenerator::new(tc, luma)); + } + + asm::dsb(); + + // enable TC0 and start! + unsafe { + VIDEO_GENERATOR.as_mut().unwrap().tc.ccr0.write(|w| { + w.clken().set_bit() + .swtrg().set_bit() + }); + } +}
diff --git a/src/video/mem.rs b/src/video/mem.rs line changes: +30/-0 index 0000000..d015662 --- /dev/null +++ b/src/video/mem.rs
@@ -0,0 +1,30 @@ +pub const ROWS: usize = 30; +pub const COLS: usize = 40; +pub const CHAR_RAM_SIZE: usize = ROWS * COLS; + +pub struct VideoRam { + chars: [u8; CHAR_RAM_SIZE], +} + +impl VideoRam { + pub const fn new() -> VideoRam { + VideoRam { + chars: [0; CHAR_RAM_SIZE], + } + } + + pub fn row_address(&self, y: usize) -> *const u8 { + &self.chars[y * COLS] as *const u8 + } + + pub fn set_cell(&mut self, x: usize, y: usize, c: u8) { + self.chars[x + y * COLS] = c; + } + + pub fn get_cell(&self, x: usize, y: usize) -> u8 { + self.chars[x + y * COLS] + } +} + +#[link_section = ".kgbss"] +pub static mut VIDEO_RAM: VideoRam = VideoRam::new();
diff --git a/src/video/mod.rs b/src/video/mod.rs line changes: +3/-0 index 0000000..7926ec1 --- /dev/null +++ b/src/video/mod.rs
@@ -0,0 +1,3 @@ +pub mod gen; +pub mod mem; +pub mod font;
diff --git a/src/video/png2font.pl b/src/video/png2font.pl line changes: +34/-0 index 0000000..bbd1c92 --- /dev/null +++ b/src/video/png2font.pl
@@ -0,0 +1,34 @@ +#!/usr/bin/perl +# Input must be 128x128 B/W PNG +use Image::Magick; +use warnings; +use strict; + +sub array_split { + my $by = shift; + my @out; + for (my $i = 0; $i < @_; $i += $by) { + push @out, [@_[$i..($i + $by - 1)]]; + } + return @out; +} + +my $image = Image::Magick->new; +$image->Read($ARGV[0]); + +print qq{pub static FONT: [[u8; 8]; 256] = [\n}; +for my $y (0..15) { + for my $x (0..15) { + my @pixels = map { + $_->[0]; + } array_split(4, $image->GetPixels(x => $x * 8, y => $y * 8, width => 8, height => 8)); + my @rows = array_split(8, @pixels); + print qq{ [\n}; + for my $r (@rows) { + my @pv = map { $_ > 0 ? '1' : '0' } @$r; + print ' 0b', @pv, ",\n"; + } + print qq{\n ],\n}; + } +} +print qq{];\n};