Last active
November 16, 2016 05:36
-
-
Save niconii/0f0ce03ca57ecb6c2317a6cde691f338 to your computer and use it in GitHub Desktop.
(WIP) Prints CrayonForth source code blocks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
crayonforth source code block format | |
each block is 256 dwords (1024 bytes), stored little-endian | |
31...24 23...16 15...8 7...0 | |
____0ttt aaaaaabb bbbbcccc ccdddddd word | |
____0000 aaaaaabb bbbbcccc ccdddddd extension of previous word | |
__ux1ttt ________ hhhhhhhh llllllll number | |
____1111 ________ ________ ________ end of data | |
t = tag/color | |
abcd = 6-bit text, right-aligned, 0-padded | |
h = number[15...8] | |
l = number[7...0] | |
u = 0: print as signed, 1: print as unsigned | |
x = 0: print as decimal, 1: print as hex | |
_ = unused | |
*/ | |
use std::result::Result as StdResult; | |
fn main() { | |
use Tag::*; | |
let mut block = Block::new(); | |
block.push_word(Label, "niconii").unwrap(); | |
block.push_number(Compile, 0x2525, true).unwrap(); | |
block.push_number(Interpret, -1, false).unwrap(); | |
block.dump_ansi(); | |
block.dump_hex(); | |
} | |
const TAG: u32 = 0x07000000; | |
const NUMBER: u32 = 0x08000000; | |
const HEX: u32 = 0x10000000; | |
const UNSIGNED: u32 = 0x20000000; | |
const C4_CHARS: [u8; 64] = | |
*br##" !"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[|]^_"## | |
; | |
fn char_to_c4(ch: char) -> u8 { | |
(ch as u8 & 0x40) >> 1 | (ch as u8 & 0x1f) | |
} | |
fn c4_to_char(ch: u8) -> char { | |
C4_CHARS[ch as usize] as char | |
} | |
fn word_to_string(mut word: u32) -> String { | |
let mut s = String::new(); | |
for _ in 0..4 { | |
let ch = ((word >> 18) & 0x3f) as u8; | |
if ch != 0 { s.push(c4_to_char(ch)); } | |
word <<= 6; | |
} | |
s | |
} | |
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
pub enum Error { | |
BlockFull, | |
OutOfRange | |
} | |
pub type Result = StdResult<(), Error>; | |
#[repr(u32)] | |
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
pub enum Tag { | |
Extension, Comment, Edit, Interpret, | |
Compile, Postpone, Wordlit, Label | |
} | |
pub struct Block { | |
buf: [u32; 256], | |
ptr: usize | |
} | |
impl Block { | |
fn new() -> Block { | |
Block { | |
buf: [0xffffffff; 256], | |
ptr: 0 | |
} | |
} | |
fn push(&mut self, w: u32) -> Result { | |
if self.ptr < 256 { | |
self.buf[self.ptr] = w; | |
self.ptr += 1; | |
Ok(()) | |
} else { | |
Err(Error::BlockFull) | |
} | |
} | |
fn push_number(&mut self, tag: Tag, n: i32, hex: bool) -> Result { | |
let u = match n { | |
32768...65535 => UNSIGNED, | |
-32768...32767 => 0, | |
_ => return Err(Error::OutOfRange) | |
}; | |
let x = if hex { HEX } else { 0 }; | |
self.push(u | x | NUMBER | (tag as u32) << 24 | n as u16 as u32) | |
} | |
fn push_word(&mut self, tag: Tag, s: &str) -> Result { | |
let mut words = vec![]; | |
let mut w = 0; | |
let chars: Vec<char> = s.chars().collect(); | |
for chunk in chars.chunks(4) { | |
for &ch in chunk { | |
w <<= 6; | |
w |= char_to_c4(ch) as u32; | |
} | |
words.push(w); | |
w = 0; | |
} | |
for (i, &word) in words.iter().enumerate() { | |
if i == 0 { | |
try!(self.push((tag as u32) << 24 | word)); | |
} else { | |
try!(self.push(word)); | |
} | |
} | |
Ok(()) | |
} | |
fn dump_ansi(&self) { | |
let mut words = self.buf.iter(); | |
while let Some(&word) = words.next() { | |
// label + number = end of block data | |
if word & (TAG | NUMBER) == (TAG | NUMBER) { | |
break | |
} | |
// read tag | |
match (word & TAG) >> 24 { | |
0 => print!("\x08"), // extension of previous word | |
1 => print!("\x1b[37;1m"), // comment (white) | |
2 => print!("\x1b[34;1m"), // edit (blue) | |
3 => print!("\x1b[33;1m"), // interpret (yellow) | |
4 => print!("\x1b[32;1m"), // compile (green) | |
5 => print!("\x1b[36;1m"), // postpone (cyan) | |
6 => print!("\x1b[35;1m"), // wordlit (orange, but magenta here) | |
_ => print!("\x1b[31;1m") // label (red) | |
} | |
// extract number | |
let number = if word & UNSIGNED != 0 { | |
word as u16 as i32 | |
} else { | |
word as i16 as i32 | |
}; | |
// check if word or number | |
match word & NUMBER { | |
0 => print!("{} ", word_to_string(word)), // word | |
_ => match word & HEX { | |
0 => print!("{} ", number), // decimal number | |
_ => print!("${:x} ", number) // hex number | |
} | |
} | |
} | |
println!("\x1b[0m"); | |
} | |
fn dump_hex(&self) { | |
for &word in self.buf.iter() { | |
if word & (TAG | NUMBER) == (TAG | NUMBER) { | |
break | |
} | |
print!("{:08x} ", word); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment