This commit is contained in:
mlokr 2024-05-11 22:22:08 +02:00
parent ebefc85566
commit b794fa7c3c
16 changed files with 392 additions and 266 deletions

View file

@ -80,7 +80,7 @@ unsafe impl BytecodeItem for u8 {}
/// ```text /// ```text
/// Types consist of letters meaning a single field /// Types consist of letters meaning a single field
/// | Type | Size (B) | Meaning | /// | Type | Size (B) | Meaning |
/// |:-----|:---------|:------------------------| /// fn():------------------------|
/// | N | 0 | Empty | /// | N | 0 | Empty |
/// | R | 1 | Register | /// | R | 1 | Register |
/// | A | 8 | Absolute address | /// | A | 8 | Absolute address |

View file

@ -1,3 +1,3 @@
main := ||: int { main := fn(): int {
return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1; return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1;
} }

View file

@ -1,13 +1,13 @@
main := ||: int { main := fn(): int {
return add_one(10) + add_two(20); return add_one(10) + add_two(20);
} }
add_two := |x: int|: int { add_two := fn(x: int): int {
return x + 2; return x + 2;
} }
add_one := |x: int|: int { add_one := fn(x: int): int {
return x + 1; return x + 1;
} }

View file

@ -1,9 +1,9 @@
main := ||: int { main := fn(): int {
return fib(10); return fib(10);
} }
fib := |x: int|: int { fib := fn(x: int): int {
if x <= 2 { if x <= 2 {
return 1; return 1;
} else { } else {

View file

@ -1,8 +1,8 @@
main := ||: int { main := fn(): int {
return fib(10); return fib(10);
} }
fib := |n: int|: int { fib := fn(n: int): int {
a := 0; a := 0;
b := 1; b := 1;
loop { loop {

View file

@ -1,3 +1,3 @@
main := ||: int { main := fn(): int {
return 1; return 1;
} }

View file

@ -1,4 +1,4 @@
main := ||: int { main := fn(): int {
a := 1; a := 1;
b := 2; b := 2;
a = a + 1; a = a + 1;

View file

@ -1,6 +1,6 @@
use { use {
crate::{ crate::{
instrs, lexer, instrs, lexer, log,
parser::{self, Expr}, parser::{self, Expr},
}, },
std::rc::Rc, std::rc::Rc,
@ -9,6 +9,42 @@ use {
type LabelId = u32; type LabelId = u32;
type Reg = u8; type Reg = u8;
type MaskElem = u64; type MaskElem = u64;
type Type = u32;
mod bt {
use super::*;
const fn builtin_type(id: u32) -> Type {
Type::MAX - id
}
macro_rules! builtin_type {
($($name:ident;)*) => {$(
pub const $name: Type = builtin_type(${index(0)});
)*};
}
builtin_type! {
INT;
BOOL;
MAX;
}
}
enum TypeKind {
Builtin(Type),
Struct(Type),
}
impl TypeKind {
fn from_ty(ty: Type) -> Self {
if ty > bt::MAX {
Self::Builtin(ty)
} else {
Self::Struct(ty)
}
}
}
const STACK_PTR: Reg = 254; const STACK_PTR: Reg = 254;
const ZERO: Reg = 0; const ZERO: Reg = 0;
@ -55,7 +91,7 @@ impl Func {
fn encode(&mut self, (len, instr): (usize, [u8; instrs::MAX_SIZE])) { fn encode(&mut self, (len, instr): (usize, [u8; instrs::MAX_SIZE])) {
let name = instrs::NAMES[instr[0] as usize]; let name = instrs::NAMES[instr[0] as usize];
println!( log::dbg!(
"{:08x}: {}: {}", "{:08x}: {}: {}",
self.code.len(), self.code.len(),
name, name,
@ -107,7 +143,7 @@ impl Func {
label.offset as i64 - reloc.offset as i64 label.offset as i64 - reloc.offset as i64
} + shift; } + shift;
dbg!( log::dbg!(
label.name.as_ref(), label.name.as_ref(),
offset, offset,
reloc.size, reloc.size,
@ -184,6 +220,11 @@ struct Loop {
relocs: Vec<RetReloc>, relocs: Vec<RetReloc>,
} }
struct Struct {
name: Rc<str>,
fields: Vec<(Rc<str>, Type)>,
}
pub struct Codegen<'a> { pub struct Codegen<'a> {
path: &'a std::path::Path, path: &'a std::path::Path,
ret: Expr<'a>, ret: Expr<'a>,
@ -196,13 +237,14 @@ pub struct Codegen<'a> {
stack_relocs: Vec<StackReloc>, stack_relocs: Vec<StackReloc>,
ret_relocs: Vec<RetReloc>, ret_relocs: Vec<RetReloc>,
loops: Vec<Loop>, loops: Vec<Loop>,
records: Vec<Struct>,
} }
impl<'a> Codegen<'a> { impl<'a> Codegen<'a> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
path: std::path::Path::new(""), path: std::path::Path::new(""),
ret: Expr::Return { val: None }, ret: Expr::Return { val: None, pos: 0 },
gpa: Default::default(), gpa: Default::default(),
code: Default::default(), code: Default::default(),
temp: Default::default(), temp: Default::default(),
@ -213,6 +255,7 @@ impl<'a> Codegen<'a> {
stack_relocs: Default::default(), stack_relocs: Default::default(),
ret_relocs: Default::default(), ret_relocs: Default::default(),
loops: Default::default(), loops: Default::default(),
records: Default::default(),
} }
} }
@ -289,9 +332,12 @@ impl<'a> Codegen<'a> {
fn expr(&mut self, expr: &'a parser::Expr<'a>, expeted: Option<Expr<'a>>) -> Option<Value<'a>> { fn expr(&mut self, expr: &'a parser::Expr<'a>, expeted: Option<Expr<'a>>) -> Option<Value<'a>> {
use {lexer::TokenKind as T, parser::Expr as E}; use {lexer::TokenKind as T, parser::Expr as E};
match *expr { match *expr {
E::Decl { E::BinOp {
name, left: E::Ident { name, .. },
val: E::Closure { ret, body, args }, op: T::Decl,
right: E::Closure {
ret, body, args, ..
},
} => { } => {
let frame = self.add_label(name); let frame = self.add_label(name);
for (i, &(name, ty)) in args.iter().enumerate() { for (i, &(name, ty)) in args.iter().enumerate() {
@ -310,8 +356,20 @@ impl<'a> Codegen<'a> {
self.ret(); self.ret();
None None
} }
E::BinOp {
left: E::Ident { name, .. },
op: T::Decl,
right,
} => {
let val = self.expr(right, None).unwrap();
let reg = self.loc_to_reg(val.loc);
let offset = self.alloc_stack(8);
self.decl_var(name, offset, val.ty);
self.store_stack(reg, offset, 8);
None
}
E::Call { E::Call {
func: E::Ident { name }, func: E::Ident { name, .. },
args, args,
} => { } => {
for (i, arg) in args.iter().enumerate() { for (i, arg) in args.iter().enumerate() {
@ -328,22 +386,14 @@ impl<'a> Codegen<'a> {
loc: Loc::Reg(reg), loc: Loc::Reg(reg),
}) })
} }
E::Decl { name, val } => { E::Ident { name, .. } => {
let val = self.expr(val, None).unwrap();
let reg = self.loc_to_reg(val.loc);
let offset = self.alloc_stack(8);
self.decl_var(name, offset, val.ty);
self.store_stack(reg, offset, 8);
None
}
E::Ident { name } => {
let var = self.vars.iter().find(|v| v.name.as_ref() == name).unwrap(); let var = self.vars.iter().find(|v| v.name.as_ref() == name).unwrap();
Some(Value { Some(Value {
ty: var.ty, ty: var.ty,
loc: Loc::Stack(var.offset), loc: Loc::Stack(var.offset),
}) })
} }
E::Return { val } => { E::Return { val, .. } => {
if let Some(val) = val { if let Some(val) = val {
let val = self.expr(val, Some(self.ret)).unwrap(); let val = self.expr(val, Some(self.ret)).unwrap();
if val.ty != self.ret { if val.ty != self.ret {
@ -365,21 +415,33 @@ impl<'a> Codegen<'a> {
self.code.encode(instrs::jmp(0)); self.code.encode(instrs::jmp(0));
None None
} }
E::Block { stmts } => { E::Block { stmts, .. } => {
for stmt in stmts { for stmt in stmts {
self.expr(stmt, None); self.expr(stmt, None);
} }
None None
} }
E::Number { value } => Some(Value { E::Number { value, .. } => Some(Value {
ty: expeted.unwrap_or(Expr::Ident { name: "int" }), ty: expeted.unwrap_or(Expr::Ident {
name: "int",
pos: 0,
}),
loc: Loc::Imm(value), loc: Loc::Imm(value),
}), }),
E::If { cond, then, else_ } => { E::If {
let cond = self.expr(cond, Some(Expr::Ident { name: "bool" })).unwrap(); cond, then, else_, ..
} => {
let cond = self
.expr(
cond,
Some(Expr::Ident {
name: "bool",
pos: 0,
}),
)
.unwrap();
let reg = self.loc_to_reg(cond.loc); let reg = self.loc_to_reg(cond.loc);
let jump_offset = self.code.code.len() as u32; let jump_offset = self.code.code.len() as u32;
println!("jump_offset: {:02x}", jump_offset);
self.code.encode(instrs::jeq(reg, 0, 0)); self.code.encode(instrs::jeq(reg, 0, 0));
self.gpa.free(reg); self.gpa.free(reg);
@ -389,7 +451,6 @@ impl<'a> Codegen<'a> {
if let Some(else_) = else_ { if let Some(else_) = else_ {
let else_jump_offset = self.code.code.len() as u32; let else_jump_offset = self.code.code.len() as u32;
println!("jump_offset: {:02x}", jump_offset);
self.code.encode(instrs::jmp(0)); self.code.encode(instrs::jmp(0));
jump = self.code.code.len() as i16 - jump_offset as i16; jump = self.code.code.len() as i16 - jump_offset as i16;
@ -397,20 +458,18 @@ impl<'a> Codegen<'a> {
self.expr(else_, None); self.expr(else_, None);
let jump = self.code.code.len() as i32 - else_jump_offset as i32; let jump = self.code.code.len() as i32 - else_jump_offset as i32;
println!("jump: {:02x}", jump);
self.code.code[else_jump_offset as usize + 1..][..4] self.code.code[else_jump_offset as usize + 1..][..4]
.copy_from_slice(&jump.to_ne_bytes()); .copy_from_slice(&jump.to_ne_bytes());
} else { } else {
jump = self.code.code.len() as i16 - jump_offset as i16; jump = self.code.code.len() as i16 - jump_offset as i16;
} }
println!("jump: {:02x}", jump);
self.code.code[jump_offset as usize + 3..][..2] self.code.code[jump_offset as usize + 3..][..2]
.copy_from_slice(&jump.to_ne_bytes()); .copy_from_slice(&jump.to_ne_bytes());
None None
} }
E::Loop { body } => { E::Loop { body, .. } => {
let loop_start = self.code.code.len() as u32; let loop_start = self.code.code.len() as u32;
self.loops.push(Loop { self.loops.push(Loop {
offset: loop_start, offset: loop_start,
@ -434,7 +493,7 @@ impl<'a> Codegen<'a> {
None None
} }
E::Break => { E::Break { .. } => {
let loop_ = self.loops.last_mut().unwrap(); let loop_ = self.loops.last_mut().unwrap();
let offset = self.code.code.len() as u32; let offset = self.code.code.len() as u32;
self.code.encode(instrs::jmp(0)); self.code.encode(instrs::jmp(0));
@ -445,7 +504,7 @@ impl<'a> Codegen<'a> {
}); });
None None
} }
E::Continue => { E::Continue { .. } => {
let loop_ = self.loops.last().unwrap(); let loop_ = self.loops.last().unwrap();
let offset = self.code.code.len() as u32; let offset = self.code.code.len() as u32;
self.code self.code
@ -468,7 +527,10 @@ impl<'a> Codegen<'a> {
self.gpa.free(rhs); self.gpa.free(rhs);
self.code.encode(instrs::cmpui(lhs, lhs, 1)); self.code.encode(instrs::cmpui(lhs, lhs, 1));
return Some(Value { return Some(Value {
ty: Expr::Ident { name: "bool" }, ty: Expr::Ident {
name: "bool",
pos: 0,
},
loc: Loc::Reg(lhs), loc: Loc::Reg(lhs),
}); });
} }
@ -478,7 +540,10 @@ impl<'a> Codegen<'a> {
self.code.encode(instrs::cmpui(lhs, lhs, 0)); self.code.encode(instrs::cmpui(lhs, lhs, 0));
self.code.encode(instrs::not(lhs, lhs)); self.code.encode(instrs::not(lhs, lhs));
return Some(Value { return Some(Value {
ty: Expr::Ident { name: "bool" }, ty: Expr::Ident {
name: "bool",
pos: 0,
},
loc: Loc::Reg(lhs), loc: Loc::Reg(lhs),
}); });
} }
@ -612,7 +677,7 @@ pub enum Loc {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::instrs; use crate::{instrs, log};
struct TestMem; struct TestMem;
@ -624,7 +689,7 @@ mod tests {
target: *mut u8, target: *mut u8,
count: usize, count: usize,
) -> Result<(), hbvm::mem::LoadError> { ) -> Result<(), hbvm::mem::LoadError> {
println!( log::dbg!(
"read: {:x} {} {:?}", "read: {:x} {} {:?}",
addr.get(), addr.get(),
count, count,
@ -646,14 +711,14 @@ mod tests {
source: *const u8, source: *const u8,
count: usize, count: usize,
) -> Result<(), hbvm::mem::StoreError> { ) -> Result<(), hbvm::mem::StoreError> {
println!("write: {:x} {}", addr.get(), count); log::dbg!("write: {:x} {}", addr.get(), count);
unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) } unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) }
Ok(()) Ok(())
} }
#[inline] #[inline]
unsafe fn prog_read<T: Copy>(&mut self, addr: hbvm::mem::Address) -> T { unsafe fn prog_read<T: Copy>(&mut self, addr: hbvm::mem::Address) -> T {
println!( log::dbg!(
"read-typed: {:x} {} {:?}", "read-typed: {:x} {} {:?}",
addr.get(), addr.get(),
std::any::type_name::<T>(), std::any::type_name::<T>(),

View file

@ -11,86 +11,103 @@ impl Token {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy)] macro_rules! gen_token_kind {
pub enum TokenKind { ($(
Ident, #[$atts:meta])*
Number, $vis:vis enum $name:ident {
LParen, #[patterns] $(
RParen, $pattern:ident,
LBrace, )*
RBrace, #[keywords] $(
LBrack, $keyword:ident = $keyword_lit:literal,
RBrack, )*
Decl, #[punkt] $(
Assign, $punkt:ident = $punkt_lit:literal,
Plus, )*
Minus, #[ops] $(
Star, #[prec = $prec:literal] $(
FSlash, $op:ident = $op_lit:literal,
Bor, )*
Or, )*
Le, }
Eq, ) => {
Semi, impl std::fmt::Display for $name {
Colon, fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Comma, let s = match *self {
Return, $( Self::$pattern => concat!('<', stringify!($pattern), '>'), )*
If,
Else, $( Self::$keyword => stringify!($keyword_lit), )*
Loop, $( Self::$punkt => stringify!($punkt_lit), )*
Break, $($( Self::$op => $op_lit, )*)*
Continue, };
Eof, f.write_str(s)
Error, }
}
impl $name {
#[inline(always)]
pub fn precedence(&self) -> Option<u8> {
Some(match self {
$($(Self::$op)|* => $prec,)*
_ => return None,
})
}
#[inline(always)]
fn from_ident(ident: &[u8]) -> Self {
match ident {
$($keyword_lit => Self::$keyword,)*
_ => Self::Ident,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
$vis enum $name {
$( $pattern, )*
$( $keyword, )*
$( $punkt, )*
$($( $op, )*)*
}
};
} }
impl std::fmt::Display for TokenKind { gen_token_kind! {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { pub enum TokenKind {
use TokenKind as T; #[patterns]
let s = match self { Ident,
T::Ident => "<identifier>", Number,
T::Number => "<number>", Eof,
T::LParen => "(", Error,
T::RParen => ")", #[keywords]
T::LBrace => "{", Return = b"return",
T::RBrace => "}", If = b"if",
T::LBrack => "[", Else = b"else",
T::RBrack => "]", Loop = b"loop",
T::Decl => ":=", Break = b"break",
T::Assign => "=", Continue = b"continue",
T::Plus => "+", Fn = b"fn",
T::Minus => "-", #[punkt]
T::Star => "*", LParen = b'(',
T::FSlash => "/", RParen = b')',
T::Bor => "|", LBrace = b'{',
T::Or => "||", RBrace = b'}',
T::Le => "<=", Semi = b';',
T::Eq => "==", Colon = b':',
T::Semi => ";", Comma = b',',
T::Colon => ":", #[ops]
T::Comma => ",", #[prec = 1]
T::Return => "return", Decl = ":=",
T::If => "if", Assign = "=",
T::Else => "else", #[prec = 21]
T::Loop => "loop", Le = "<=",
T::Break => "break", Eq = "==",
T::Continue => "continue", #[prec = 23]
T::Eof => "<eof>", Plus = "+",
T::Error => "<error>", Minus = "-",
}; #[prec = 24]
write!(f, "{}", s) Star = "*",
} FSlash = "/",
}
impl TokenKind {
pub fn precedence(&self) -> Option<u8> {
Some(match self {
Self::Assign => 1,
Self::Le | Self::Eq => 21,
Self::Plus | Self::Minus => 23,
Self::Star | Self::FSlash => 24,
_ => return None,
})
} }
} }
@ -174,44 +191,23 @@ impl<'a> Iterator for Lexer<'a> {
} }
let ident = &self.bytes[start as usize..self.pos as usize]; let ident = &self.bytes[start as usize..self.pos as usize];
match ident { T::from_ident(ident)
b"return" => T::Return,
b"if" => T::If,
b"else" => T::Else,
b"loop" => T::Loop,
b"break" => T::Break,
b"continue" => T::Continue,
_ => T::Ident,
}
} }
b':' => match self.advance_if(b'=') { b':' if self.advance_if(b'=') => T::Decl,
true => T::Decl, b':' => T::Colon,
false => T::Colon,
},
b',' => T::Comma, b',' => T::Comma,
b';' => T::Semi, b';' => T::Semi,
b'=' => match self.advance_if(b'=') { b'=' if self.advance_if(b'=') => T::Eq,
true => T::Eq, b'=' => T::Assign,
false => T::Assign, b'<' if self.advance_if(b'=') => T::Le,
},
b'<' => match self.advance_if(b'=') {
true => T::Le,
false => T::Error,
},
b'+' => T::Plus, b'+' => T::Plus,
b'-' => T::Minus, b'-' => T::Minus,
b'*' => T::Star, b'*' => T::Star,
b'/' => T::FSlash, b'/' => T::FSlash,
b'|' => match self.advance_if(b'|') {
true => T::Or,
false => T::Bor,
},
b'(' => T::LParen, b'(' => T::LParen,
b')' => T::RParen, b')' => T::RParen,
b'{' => T::LBrace, b'{' => T::LBrace,
b'}' => T::RBrace, b'}' => T::RBrace,
b'[' => T::LBrack,
b']' => T::RBrack,
_ => T::Error, _ => T::Error,
}; };

View file

@ -1,4 +1,5 @@
#![feature(noop_waker)] #![feature(noop_waker)]
#![feature(macro_metavar_expr)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(non_null_convenience)] #![feature(non_null_convenience)]
#![allow(dead_code)] #![allow(dead_code)]
@ -18,6 +19,7 @@ mod codegen;
mod ident; mod ident;
mod instrs; mod instrs;
mod lexer; mod lexer;
mod log;
mod parser; mod parser;
mod tests; mod tests;
mod typechk; mod typechk;

48
hblang/src/log.rs Normal file
View file

@ -0,0 +1,48 @@
#![allow(unused_macros)]
#[derive(PartialOrd, PartialEq, Ord, Eq, Debug)]
pub enum Level {
Err,
Wrn,
Inf,
Dbg,
}
pub const LOG_LEVEL: Level = match option_env!("LOG_LEVEL") {
Some(val) => match val.as_bytes()[0] {
b'e' => Level::Err,
b'w' => Level::Wrn,
b'i' => Level::Inf,
b'd' => Level::Dbg,
_ => panic!("Invalid log level."),
},
None => {
if cfg!(debug_assertions) {
Level::Dbg
} else {
Level::Err
}
}
};
macro_rules! log {
($level:expr, $fmt:literal $($expr:tt)*) => {
if $level <= $crate::log::LOG_LEVEL {
println!("{:?}: {}", $level, format_args!($fmt $($expr)*));
}
};
($level:expr, $($arg:expr),*) => {
if $level <= $crate::log::LOG_LEVEL {
$(println!("[{}{}{}][{:?}]: {} = {:?}", line!(), column!(), file!(), $level, stringify!($arg), $arg);)*
}
};
}
macro_rules! err { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Err, $($arg)*) }; }
macro_rules! wrn { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Wrn, $($arg)*) }; }
macro_rules! inf { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Inf, $($arg)*) }; }
macro_rules! dbg { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Dbg, $($arg)*) }; }
#[allow(unused_imports)]
pub(crate) use {dbg, err, inf, log, wrn};

View file

@ -70,69 +70,61 @@ impl<'a, 'b> Parser<'a, 'b> {
} }
fn unit_expr(&mut self) -> Expr<'a> { fn unit_expr(&mut self) -> Expr<'a> {
use {Expr as E, TokenKind as T};
let token = self.next(); let token = self.next();
let mut expr = match token.kind { let mut expr = match token.kind {
TokenKind::Ident => { T::Ident => E::Ident {
let name = self.arena.alloc_str(self.lexer.slice(token)); pos: token.start,
if self.advance_if(TokenKind::Decl) { name: self.arena.alloc_str(self.lexer.slice(token)),
let val = self.ptr_expr(); },
Expr::Decl { name, val } T::If => E::If {
} else { pos: token.start,
Expr::Ident { name } cond: self.ptr_expr(),
} then: self.ptr_expr(),
} else_: self.advance_if(T::Else).then(|| self.ptr_expr()),
TokenKind::If => { },
let cond = self.ptr_expr(); T::Loop => E::Loop {
let then = self.ptr_expr(); pos: token.start,
let else_ = self.advance_if(TokenKind::Else).then(|| self.ptr_expr());
Expr::If { cond, then, else_ }
}
TokenKind::Loop => Expr::Loop {
body: self.ptr_expr(), body: self.ptr_expr(),
}, },
TokenKind::Break => Expr::Break, T::Break => E::Break { pos: token.start },
TokenKind::Continue => Expr::Continue, T::Continue => E::Continue { pos: token.start },
TokenKind::Return => Expr::Return { T::Return => E::Return {
val: (self.token.kind != TokenKind::Semi).then(|| self.ptr_expr()), pos: token.start,
val: (self.token.kind != T::Semi).then(|| self.ptr_expr()),
}, },
TokenKind::Or => { T::Fn => E::Closure {
self.expect_advance(TokenKind::Colon); pos: token.start,
let ret = self.ptr_expr(); args: {
let body = self.ptr_expr(); self.expect_advance(T::LParen);
Expr::Closure { self.collect_list(T::Comma, T::RParen, |s| {
ret, let name = s.expect_advance(T::Ident);
body,
args: &[],
}
}
TokenKind::Bor => {
let args = self.collect(|s| {
s.advance_if(TokenKind::Bor).not().then(|| {
let name = s.expect_advance(TokenKind::Ident);
let name = s.arena.alloc_str(s.lexer.slice(name)); let name = s.arena.alloc_str(s.lexer.slice(name));
s.expect_advance(TokenKind::Colon); s.expect_advance(T::Colon);
let val = s.expr(); let val = s.expr();
s.advance_if(TokenKind::Comma);
(name, val) (name, val)
}) })
}); },
self.expect_advance(TokenKind::Colon); ret: {
let ret = self.ptr_expr(); self.expect_advance(T::Colon);
let body = self.ptr_expr(); self.ptr_expr()
Expr::Closure { args, ret, body } },
} body: self.ptr_expr(),
TokenKind::LBrace => Expr::Block {
stmts: self.collect(|s| (!s.advance_if(TokenKind::RBrace)).then(|| s.expr())),
}, },
TokenKind::Number => Expr::Number { T::LBrace => E::Block {
pos: token.start,
stmts: self.collect_list(T::Semi, T::RBrace, Self::expr),
},
T::Number => E::Number {
pos: token.start,
value: match self.lexer.slice(token).parse() { value: match self.lexer.slice(token).parse() {
Ok(value) => value, Ok(value) => value,
Err(e) => self.report(format_args!("invalid number: {e}")), Err(e) => self.report(format_args!("invalid number: {e}")),
}, },
}, },
TokenKind::LParen => { T::LParen => {
let expr = self.expr(); let expr = self.expr();
self.expect_advance(TokenKind::RParen); self.expect_advance(T::RParen);
expr expr
} }
tok => self.report(format_args!("unexpected token: {tok:?}")), tok => self.report(format_args!("unexpected token: {tok:?}")),
@ -144,13 +136,7 @@ impl<'a, 'b> Parser<'a, 'b> {
self.next(); self.next();
Expr::Call { Expr::Call {
func: self.arena.alloc(expr), func: self.arena.alloc(expr),
args: self.collect(|s| { args: self.collect_list(TokenKind::Comma, TokenKind::RParen, Self::expr),
s.advance_if(TokenKind::RParen).not().then(|| {
let arg = s.expr();
s.advance_if(TokenKind::Comma);
arg
})
}),
} }
} }
_ => break, _ => break,
@ -162,6 +148,21 @@ impl<'a, 'b> Parser<'a, 'b> {
expr expr
} }
fn collect_list<T: Copy>(
&mut self,
delim: TokenKind,
end: TokenKind,
mut f: impl FnMut(&mut Self) -> T,
) -> &'a [T] {
self.collect(|s| {
s.advance_if(end).not().then(|| {
let val = f(s);
s.advance_if(delim);
val
})
})
}
fn collect<T: Copy>(&mut self, mut f: impl FnMut(&mut Self) -> Option<T>) -> &'a [T] { fn collect<T: Copy>(&mut self, mut f: impl FnMut(&mut Self) -> Option<T>) -> &'a [T] {
let vec = std::iter::from_fn(|| f(self)).collect::<Vec<_>>(); let vec = std::iter::from_fn(|| f(self)).collect::<Vec<_>>();
self.arena.alloc_slice(&vec) self.arena.alloc_slice(&vec)
@ -195,13 +196,14 @@ impl<'a, 'b> Parser<'a, 'b> {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Expr<'a> { pub enum Expr<'a> {
Break, Break {
Continue, pos: u32,
Decl { },
name: &'a str, Continue {
val: &'a Expr<'a>, pos: u32,
}, },
Closure { Closure {
pos: u32,
args: &'a [(&'a str, Expr<'a>)], args: &'a [(&'a str, Expr<'a>)],
ret: &'a Expr<'a>, ret: &'a Expr<'a>,
body: &'a Expr<'a>, body: &'a Expr<'a>,
@ -211,15 +213,19 @@ pub enum Expr<'a> {
args: &'a [Expr<'a>], args: &'a [Expr<'a>],
}, },
Return { Return {
pos: u32,
val: Option<&'a Expr<'a>>, val: Option<&'a Expr<'a>>,
}, },
Ident { Ident {
pos: u32,
name: &'a str, name: &'a str,
}, },
Block { Block {
pos: u32,
stmts: &'a [Expr<'a>], stmts: &'a [Expr<'a>],
}, },
Number { Number {
pos: u32,
value: u64, value: u64,
}, },
BinOp { BinOp {
@ -228,11 +234,13 @@ pub enum Expr<'a> {
right: &'a Expr<'a>, right: &'a Expr<'a>,
}, },
If { If {
pos: u32,
cond: &'a Expr<'a>, cond: &'a Expr<'a>,
then: &'a Expr<'a>, then: &'a Expr<'a>,
else_: Option<&'a Expr<'a>>, else_: Option<&'a Expr<'a>>,
}, },
Loop { Loop {
pos: u32,
body: &'a Expr<'a>, body: &'a Expr<'a>,
}, },
} }
@ -244,18 +252,21 @@ impl<'a> std::fmt::Display for Expr<'a> {
} }
match *self { match *self {
Self::Break => write!(f, "break;"), Self::Break { .. } => write!(f, "break;"),
Self::Continue => write!(f, "continue;"), Self::Continue { .. } => write!(f, "continue;"),
Self::If { cond, then, else_ } => { Self::If {
cond, then, else_, ..
} => {
write!(f, "if {} {}", cond, then)?; write!(f, "if {} {}", cond, then)?;
if let Some(else_) = else_ { if let Some(else_) = else_ {
write!(f, " else {}", else_)?; write!(f, " else {}", else_)?;
} }
Ok(()) Ok(())
} }
Self::Loop { body } => write!(f, "loop {}", body), Self::Loop { body, .. } => write!(f, "loop {}", body),
Self::Decl { name, val } => write!(f, "{} := {}", name, val), Self::Closure {
Self::Closure { ret, body, args } => { ret, body, args, ..
} => {
write!(f, "|")?; write!(f, "|")?;
let first = &mut true; let first = &mut true;
for (name, val) in args { for (name, val) in args {
@ -277,10 +288,10 @@ impl<'a> std::fmt::Display for Expr<'a> {
} }
write!(f, ")") write!(f, ")")
} }
Self::Return { val: Some(val) } => write!(f, "return {};", val), Self::Return { val: Some(val), .. } => write!(f, "return {};", val),
Self::Return { val: None } => write!(f, "return;"), Self::Return { val: None, .. } => write!(f, "return;"),
Self::Ident { name } => write!(f, "{}", name), Self::Ident { name, .. } => write!(f, "{}", name),
Self::Block { stmts } => { Self::Block { stmts, .. } => {
writeln!(f, "{{")?; writeln!(f, "{{")?;
INDENT.with(|i| i.set(i.get() + 1)); INDENT.with(|i| i.set(i.get() + 1));
let res = (|| { let res = (|| {
@ -296,7 +307,7 @@ impl<'a> std::fmt::Display for Expr<'a> {
write!(f, "}}")?; write!(f, "}}")?;
res res
} }
Self::Number { value } => write!(f, "{}", value), Self::Number { value, .. } => write!(f, "{}", value),
Self::BinOp { left, right, op } => { Self::BinOp { left, right, op } => {
let display_branch = |f: &mut std::fmt::Formatter, expr: &Self| { let display_branch = |f: &mut std::fmt::Formatter, expr: &Self| {
if let Self::BinOp { op: lop, .. } = expr if let Self::BinOp { op: lop, .. } = expr

Binary file not shown.

View file

@ -1,6 +1,8 @@
Ident "main" Ident "main"
Decl ":=" Decl ":="
Or "||" Fn "fn"
LParen "("
RParen ")"
Colon ":" Colon ":"
Ident "int" Ident "int"
LBrace "{" LBrace "{"

View file

@ -1,6 +1,8 @@
Ident "main" Ident "main"
Decl ":=" Decl ":="
Or "||" Fn "fn"
LParen "("
RParen ")"
Colon ":" Colon ":"
Ident "int" Ident "int"
LBrace "{" LBrace "{"

84
spec.md
View file

@ -50,7 +50,7 @@ of offset in the code. Not from the beginning of current or following instructio
## Rounding modes ## Rounding modes
| Rounding mode | Value | | Rounding mode | Value |
|:-------------------------|:------| fn():------|
| To nearest, ties to even | 0b00 | | To nearest, ties to even | 0b00 |
| Towards 0 (truncate) | 0b01 | | Towards 0 (truncate) | 0b01 |
| Towards +∞ (up) | 0b10 | | Towards +∞ (up) | 0b10 |
@ -89,7 +89,7 @@ Program counter stays on the currently executed instruction
- Type `N` - Type `N`
| Opcode | Mnemonic | Action | | Opcode | Mnemonic | Action |
|:-------|:---------|:--------------------------------------------| fn():--------------------------------------------|
| 0x00 | UN | Throw unreachable code exception | | 0x00 | UN | Throw unreachable code exception |
| 0x01 | TX | Terminate execution (eg. on end of program) | | 0x01 | TX | Terminate execution (eg. on end of program) |
| 0x02 | NOP | Do nothing | | 0x02 | NOP | Do nothing |
@ -100,7 +100,7 @@ Program counter stays on the currently executed instruction
## Addition (`+`) ## Addition (`+`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x03 | ADD8 | Xi8 | | 0x03 | ADD8 | Xi8 |
| 0x04 | ADD16 | Xi16 | | 0x04 | ADD16 | Xi16 |
| 0x05 | ADD32 | Xi32 | | 0x05 | ADD32 | Xi32 |
@ -108,7 +108,7 @@ Program counter stays on the currently executed instruction
## Subtraction (`-`) ## Subtraction (`-`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x07 | SUB8 | Xi8 | | 0x07 | SUB8 | Xi8 |
| 0x08 | SUB16 | Xi16 | | 0x08 | SUB16 | Xi16 |
| 0x09 | SUB32 | Xi32 | | 0x09 | SUB32 | Xi32 |
@ -116,7 +116,7 @@ Program counter stays on the currently executed instruction
## Multiplication (`*`) ## Multiplication (`*`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x0B | MUL8 | Xi8 | | 0x0B | MUL8 | Xi8 |
| 0x0C | MUL16 | Xi16 | | 0x0C | MUL16 | Xi16 |
| 0x0D | MUL32 | Xi32 | | 0x0D | MUL32 | Xi32 |
@ -124,14 +124,14 @@ Program counter stays on the currently executed instruction
## Bitwise ops (type: Xi64) ## Bitwise ops (type: Xi64)
| Opcode | Mnemonic | Operation | | Opcode | Mnemonic | Operation |
|:-------|:---------|:--------------------| fn():--------------------|
| 0x0F | AND | Conjunction (&) | | 0x0F | AND | Conjunction (&) |
| 0x10 | OR | Disjunction (\|) | | 0x10 | OR | Disjunction (\|) |
| 0x11 | XOR | Non-equivalence (^) | | 0x11 | XOR | Non-equivalence (^) |
## Unsigned left bitshift (`<<`) ## Unsigned left bitshift (`<<`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x12 | SLU8 | Ui8 | | 0x12 | SLU8 | Ui8 |
| 0x13 | SLU16 | Ui16 | | 0x13 | SLU16 | Ui16 |
| 0x14 | SLU32 | Ui32 | | 0x14 | SLU32 | Ui32 |
@ -139,7 +139,7 @@ Program counter stays on the currently executed instruction
## Unsigned right bitshift (`>>`) ## Unsigned right bitshift (`>>`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x16 | SRU8 | Ui8 | | 0x16 | SRU8 | Ui8 |
| 0x17 | SRU16 | Ui16 | | 0x17 | SRU16 | Ui16 |
| 0x18 | SRU32 | Ui32 | | 0x18 | SRU32 | Ui32 |
@ -147,7 +147,7 @@ Program counter stays on the currently executed instruction
## Signed right bitshift (`>>`) ## Signed right bitshift (`>>`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x1A | SRS8 | Si8 | | 0x1A | SRS8 | Si8 |
| 0x1B | SRS16 | Si16 | | 0x1B | SRS16 | Si16 |
| 0x1C | SRS32 | Si32 | | 0x1C | SRS32 | Si32 |
@ -158,13 +158,13 @@ Program counter stays on the currently executed instruction
- Operation: `#0 ← #1 <=> #2` - Operation: `#0 ← #1 <=> #2`
| Ordering | Number | | Ordering | Number |
|:---------|:-------| fn():-------|
| < | -1 | | < | -1 |
| = | 0 | | = | 0 |
| > | 1 | | > | 1 |
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x1E | CMPU | Ui64 | | 0x1E | CMPU | Ui64 |
| 0x1F | CMPS | Si64 | | 0x1F | CMPS | Si64 |
@ -179,7 +179,7 @@ Program counter stays on the currently executed instruction
- `#1 ← #2` - `#1 ← #2`
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x20 | DIRU8 | Ui8 | | 0x20 | DIRU8 | Ui8 |
| 0x21 | DIRU16 | Ui16 | | 0x21 | DIRU16 | Ui16 |
| 0x22 | DIRU32 | Ui32 | | 0x22 | DIRU32 | Ui32 |
@ -194,7 +194,7 @@ Program counter stays on the currently executed instruction
- Operation: `#0 ← <OP> #1` - Operation: `#0 ← <OP> #1`
| Opcode | Mnemonic | Operation | | Opcode | Mnemonic | Operation |
|:-------|:---------|:-------------------------| fn():-------------------------|
| 0x28 | NEG | Bitwise complement (`~`) | | 0x28 | NEG | Bitwise complement (`~`) |
| 0x29 | NOT | Logical negation (`!`) | | 0x29 | NOT | Logical negation (`!`) |
@ -202,7 +202,7 @@ Program counter stays on the currently executed instruction
- Operation: `#0 ← Si64(#1)` - Operation: `#0 ← Si64(#1)`
| Opcode | Mnemonic | Source type | | Opcode | Mnemonic | Source type |
|:-------|:---------|:------------| fn():------------|
| 0x2A | SXT8 | Si8 | | 0x2A | SXT8 | Si8 |
| 0x2B | SXT16 | Si16 | | 0x2B | SXT16 | Si16 |
| 0x2C | SXT32 | Si32 | | 0x2C | SXT32 | Si32 |
@ -213,7 +213,7 @@ Program counter stays on the currently executed instruction
## Addition (`+`) ## Addition (`+`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x2D | ADDI8 | Xi8 | | 0x2D | ADDI8 | Xi8 |
| 0x2E | ADDI16 | Xi16 | | 0x2E | ADDI16 | Xi16 |
| 0x2F | ADDI32 | Xi32 | | 0x2F | ADDI32 | Xi32 |
@ -221,7 +221,7 @@ Program counter stays on the currently executed instruction
## Multiplication (`*`) ## Multiplication (`*`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x31 | MULI8 | Xi8 | | 0x31 | MULI8 | Xi8 |
| 0x32 | MULI16 | Xi16 | | 0x32 | MULI16 | Xi16 |
| 0x33 | MULI32 | Xi32 | | 0x33 | MULI32 | Xi32 |
@ -229,7 +229,7 @@ Program counter stays on the currently executed instruction
## Bitwise ops (type: Xi64) ## Bitwise ops (type: Xi64)
| Opcode | Mnemonic | Operation | | Opcode | Mnemonic | Operation |
|:-------|:---------|:--------------------| fn():--------------------|
| 0x35 | ANDI | Conjunction (&) | | 0x35 | ANDI | Conjunction (&) |
| 0x36 | ORI | Disjunction (\|) | | 0x36 | ORI | Disjunction (\|) |
| 0x37 | XORI | Non-equivalence (^) | | 0x37 | XORI | Non-equivalence (^) |
@ -240,7 +240,7 @@ Program counter stays on the currently executed instruction
## Unsigned left bitshift (`<<`) ## Unsigned left bitshift (`<<`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x38 | SLUI8 | Ui8 | | 0x38 | SLUI8 | Ui8 |
| 0x39 | SLUI16 | Ui16 | | 0x39 | SLUI16 | Ui16 |
| 0x3A | SLUI32 | Ui32 | | 0x3A | SLUI32 | Ui32 |
@ -248,7 +248,7 @@ Program counter stays on the currently executed instruction
## Unsigned right bitshift (`>>`) ## Unsigned right bitshift (`>>`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x3C | SRUI8 | Ui8 | | 0x3C | SRUI8 | Ui8 |
| 0x3D | SRUI16 | Ui16 | | 0x3D | SRUI16 | Ui16 |
| 0x3E | SRUI32 | Ui32 | | 0x3E | SRUI32 | Ui32 |
@ -256,7 +256,7 @@ Program counter stays on the currently executed instruction
## Signed right bitshift (`>>`) ## Signed right bitshift (`>>`)
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x40 | SRSI8 | Si8 | | 0x40 | SRSI8 | Si8 |
| 0x41 | SRSI16 | Si16 | | 0x41 | SRSI16 | Si16 |
| 0x42 | SRSI32 | Si32 | | 0x42 | SRSI32 | Si32 |
@ -268,7 +268,7 @@ Program counter stays on the currently executed instruction
- Comparsion table same for register-register one - Comparsion table same for register-register one
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x44 | CMPUI | Ui64 | | 0x44 | CMPUI | Ui64 |
| 0x45 | CMPSI | Si64 | | 0x45 | CMPSI | Si64 |
@ -276,7 +276,7 @@ Program counter stays on the currently executed instruction
- Type: `RR` - Type: `RR`
| Opcode | Mnemonic | Operation | | Opcode | Mnemonic | Operation |
|:-------|:---------|:---------------------------------| fn():---------------------------------|
| 0x46 | CP | Copy register value (`#0 ← #1`) | | 0x46 | CP | Copy register value (`#0 ← #1`) |
| 0x47 | SWA | Swap register values (`#0 ⇆ #1`) | | 0x47 | SWA | Swap register values (`#0 ⇆ #1`) |
@ -286,7 +286,7 @@ Program counter stays on the currently executed instruction
- Operation: `#0 ← $1` - Operation: `#0 ← $1`
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x48 | LI8 | Xi8 | | 0x48 | LI8 | Xi8 |
| 0x49 | LI16 | Xi16 | | 0x49 | LI16 | Xi16 |
| 0x4A | Li32 | Xi32 | | 0x4A | Li32 | Xi32 |
@ -298,7 +298,7 @@ Program counter stays on the currently executed instruction
- Operation: `#0 ← pc + #1 + $2` - Operation: `#0 ← pc + #1 + $2`
| Opcode | Mnemonic | | Opcode | Mnemonic |
|:-------|:---------| fn():---------|
| 0x4C | LRA | | 0x4C | LRA |
# Memory access operations # Memory access operations
@ -313,7 +313,7 @@ Program counter stays on the currently executed instruction
- Computes address from base register and absolute offset - Computes address from base register and absolute offset
| Opcode | Mnemonic | Operation | | Opcode | Mnemonic | Operation |
|:-------|:---------|:-------------------| fn():-------------------|
| 0x4D | LD | `#0 ← $3[#1 + $2]` | | 0x4D | LD | `#0 ← $3[#1 + $2]` |
| 0x4E | ST | `$3[#1 + $2] ← #0` | | 0x4E | ST | `$3[#1 + $2] ← #0` |
@ -322,7 +322,7 @@ Program counter stays on the currently executed instruction
- Computes address from register and offset from program counter - Computes address from register and offset from program counter
| Opcode | Mnemonic | Operation | | Opcode | Mnemonic | Operation |
|:-------|:---------|:------------------------| fn():------------------------|
| 0x4F | LDR | `#0 ← $3[pc + #1 + $2]` | | 0x4F | LDR | `#0 ← $3[pc + #1 + $2]` |
| 0x50 | STR | `$3[pc + #1 + $2] ← #0` | | 0x50 | STR | `$3[pc + #1 + $2] ← #0` |
@ -331,7 +331,7 @@ Program counter stays on the currently executed instruction
- Copies block of `$3` bytes from memory location on address on `#0` to `#1` - Copies block of `$3` bytes from memory location on address on `#0` to `#1`
| Opcode | Mnemonic | Operation | | Opcode | Mnemonic | Operation |
|:-------|:---------|:------------------| fn():------------------|
| 0x51 | BMC | `$3[#1] ← $3[x0]` | | 0x51 | BMC | `$3[#1] ← $3[x0]` |
# Block register copy # Block register copy
@ -340,14 +340,14 @@ Program counter stays on the currently executed instruction
- Copying over the 256 registers causes an exception - Copying over the 256 registers causes an exception
| Opcode | Mnemonic | Operation | | Opcode | Mnemonic | Operation |
|:-------|:---------|:--------------| fn():--------------|
| 0x52 | BRC | `$3#1 ← $3#0` | | 0x52 | BRC | `$3#1 ← $3#0` |
# Relative jump # Relative jump
- Type: `O` - Type: `O`
| Opcode | Mnemonic | Operation | | Opcode | Mnemonic | Operation |
|:-------|:---------|:---------------| fn():---------------|
| 0x53 | JMP | `pc ← pc + $0` | | 0x53 | JMP | `pc ← pc + $0` |
# Linking jump # Linking jump
@ -357,7 +357,7 @@ Program counter stays on the currently executed instruction
- Jump to specified address - Jump to specified address
| Opcode | Mnemonic | Instruction type | Address | | Opcode | Mnemonic | Instruction type | Address |
|:-------|:---------|:------------------|:-------------------------| fn():-------------------------|
| 0x54 | JAL | RRO (size = 6 B) | Relative, `pc + #1 + $2` | | 0x54 | JAL | RRO (size = 6 B) | Relative, `pc + #1 + $2` |
| 0x55 | JALA | RRA (size = 10 B) | Absolute, `#1 + $2` | | 0x55 | JALA | RRA (size = 10 B) | Absolute, `#1 + $2` |
@ -367,7 +367,7 @@ Program counter stays on the currently executed instruction
- Operation: `if #0 <CMP> #1 { pc ← pc + $2 }` - Operation: `if #0 <CMP> #1 { pc ← pc + $2 }`
| Opcode | Mnemonic | Condition | Type | | Opcode | Mnemonic | Condition | Type |
|:-------|:---------|:-------------------|:-----| fn():-----|
| 0x56 | JEQ | Equals (`=`) | Xi64 | | 0x56 | JEQ | Equals (`=`) | Xi64 |
| 0x57 | JNE | Not-equals (`≠`) | Xi64 | | 0x57 | JNE | Not-equals (`≠`) | Xi64 |
| 0x58 | JLTU | Less-than (`<`) | Ui64 | | 0x58 | JLTU | Less-than (`<`) | Ui64 |
@ -380,7 +380,7 @@ Program counter stays on the currently executed instruction
- Type: `N` - Type: `N`
| Opcode | Mnemonic | Trap type | | Opcode | Mnemonic | Trap type |
|:-------|:---------|:-----------------| fn():-----------------|
| 0x5C | ECA | Environment call | | 0x5C | ECA | Environment call |
| 0x5D | EBP | Breakpoint | | 0x5D | EBP | Breakpoint |
@ -389,7 +389,7 @@ Program counter stays on the currently executed instruction
- Operation: `#0 ← #1 <OP> #2` - Operation: `#0 ← #1 <OP> #2`
| Opcode | Mnemonic | Operation | Type | | Opcode | Mnemonic | Operation | Type |
|:-------|:---------|:---------------------|:-----| fn():-----|
| 0x5E | FADD32 | Addition (`+`) | Fl32 | | 0x5E | FADD32 | Addition (`+`) | Fl32 |
| 0x5F | FADD64 | Addition (`+`) | Fl64 | | 0x5F | FADD64 | Addition (`+`) | Fl64 |
| 0x60 | FSUB32 | Subtraction (`-`) | Fl32 | | 0x60 | FSUB32 | Subtraction (`-`) | Fl32 |
@ -404,7 +404,7 @@ Program counter stays on the currently executed instruction
- Operation: `#0 ← (#1 * #2) + #3` - Operation: `#0 ← (#1 * #2) + #3`
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x66 | FMA32 | Fl32 | | 0x66 | FMA32 | Fl32 |
| 0x67 | FMA64 | Fl64 | | 0x67 | FMA64 | Fl64 |
@ -415,7 +415,7 @@ Program counter stays on the currently executed instruction
- NaN is less-than/greater-than depends on variant - NaN is less-than/greater-than depends on variant
| Opcode | Mnemonic | Type | NaN is | | Opcode | Mnemonic | Type | NaN is |
|:-------|:---------|:-----|:-------| fn():-------|
| 0x6A | FCMPLT32 | Fl32 | < | | 0x6A | FCMPLT32 | Fl32 | < |
| 0x6B | FCMPLT64 | Fl64 | < | | 0x6B | FCMPLT64 | Fl64 | < |
| 0x6C | FCMPGT32 | Fl32 | > | | 0x6C | FCMPGT32 | Fl32 | > |
@ -427,7 +427,7 @@ Program counter stays on the currently executed instruction
- Operation: `#0 ← Fl<SIZE>(#1)` - Operation: `#0 ← Fl<SIZE>(#1)`
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x6E | ITF32 | Fl32 | | 0x6E | ITF32 | Fl32 |
| 0x6F | ITF64 | Fl64 | | 0x6F | ITF64 | Fl64 |
@ -437,7 +437,7 @@ Program counter stays on the currently executed instruction
- Immediate `$2` specifies rounding mode - Immediate `$2` specifies rounding mode
| Opcode | Mnemonic | Type | | Opcode | Mnemonic | Type |
|:-------|:---------|:-----| fn():-----|
| 0x70 | FTI32 | Fl32 | | 0x70 | FTI32 | Fl32 |
| 0x71 | FTI64 | Fl64 | | 0x71 | FTI64 | Fl64 |
@ -446,7 +446,7 @@ Program counter stays on the currently executed instruction
- Operation: `#0 ← Fl64(#1)` - Operation: `#0 ← Fl64(#1)`
| Opcode | Mnemonic | | Opcode | Mnemonic |
|:-------|:---------| fn():---------|
| 0x72 | FC32T64 | | 0x72 | FC32T64 |
# Fl64 to Fl32 # Fl64 to Fl32
@ -455,13 +455,13 @@ Program counter stays on the currently executed instruction
- Immediate `$2` specified rounding mode - Immediate `$2` specified rounding mode
| Opcode | Mnemonic | | Opcode | Mnemonic |
|:-------|:---------| fn():---------|
| 0x73 | FC64T32 | | 0x73 | FC64T32 |
# 16-bit relative address instruction variants # 16-bit relative address instruction variants
| Opcode | Mnemonic | Type | Variant of | | Opcode | Mnemonic | Type | Variant of |
|:-------|:---------|:-----|:-----------| fn():-----------|
| 0x74 | LRA16 | RRP | LRA | | 0x74 | LRA16 | RRP | LRA |
| 0x75 | LDR16 | RRPH | LDR | | 0x75 | LDR16 | RRPH | LDR |
| 0x76 | STR16 | RRPH | STR | | 0x76 | STR16 | RRPH | STR |
@ -472,7 +472,7 @@ Program counter stays on the currently executed instruction
- One byte is 8 bits - One byte is 8 bits
| C Type | Description | Byte sizes | | C Type | Description | Byte sizes |
|:------------|:-------------------------|:-----------| fn():-----------|
| char | Character / byte | 1 | | char | Character / byte | 1 |
| short | Short integer | 2 | | short | Short integer | 2 |
| int | Integer | 4 | | int | Integer | 4 |
@ -491,7 +491,7 @@ Program counter stays on the currently executed instruction
- Registers r32 r255 are callee saved - Registers r32 r255 are callee saved
| Register | Description | Saver | | Register | Description | Saver |
|:-----------|:--------------------|:-------| fn():-------|
| r0 | Hard-wired zero | N/A | | r0 | Hard-wired zero | N/A |
| r1 - r2 | Return values | Caller | | r1 - r2 | Return values | Caller |
| r2 - r11 | Function parameters | Caller | | r2 - r11 | Function parameters | Caller |