initial commit
This commit is contained in:
commit
45b9dc1790
5
Cargo.lock
generated
Normal file
5
Cargo.lock
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "engine25519-as"
|
||||||
|
version = "0.1.0"
|
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "engine25519-as"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["bunnie <bunnie@kosagi.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[workspace]
|
317
src/lib.rs
Normal file
317
src/lib.rs
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
#![feature(trace_macros)]
|
||||||
|
#![feature(log_syntax)]
|
||||||
|
trace_macros!{true}
|
||||||
|
|
||||||
|
/*
|
||||||
|
opcodes
|
||||||
|
|
||||||
|
PSA Wd, Ra
|
||||||
|
PSB Wd, Rb
|
||||||
|
MSK Wd, Ra, Rb
|
||||||
|
XOR Wd, Ra, Rb
|
||||||
|
NOT Wd, Ra
|
||||||
|
ADD Wd, Ra, Rb
|
||||||
|
SUB Wd, Ra, Rb
|
||||||
|
MUL Wd, Ra, Rb
|
||||||
|
TRD Wd, Ra, Rb
|
||||||
|
BRZ offset, Ra
|
||||||
|
|
||||||
|
all Rx can be expressed as #Rx which summons the constant at #Rx
|
||||||
|
|
||||||
|
examples of syntax
|
||||||
|
ADD r0, r1, r2 // adds r1 and r2, stores into r0
|
||||||
|
ADD r31, #4, #8 // takes constants in table positions 4 and 8, adds them, stores into r31
|
||||||
|
PSA r1, #0 // takes constant in table position 0, stores in r1
|
||||||
|
BRZ -0x3, r4 // if r4 is 0, mpc = mpc - 0x3
|
||||||
|
BRZ 0x2, r1 // if r1 is 0, mpc = mpc + 0x2
|
||||||
|
loop:
|
||||||
|
BRZ loop, r2 // if r2 is 0, go to label "loop"
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// A compile-time map from identifiers to arbitrary (heterogeneous) expressions
|
||||||
|
#[macro_export]
|
||||||
|
#[doc(hidden)]
|
||||||
|
macro_rules! ident_map {
|
||||||
|
( $name:ident = { $($key:ident => $e:expr),* $(,)* } ) => {
|
||||||
|
macro_rules! $name {
|
||||||
|
$(
|
||||||
|
( $key ) => { $e };
|
||||||
|
)*
|
||||||
|
// Empty invocation expands to nothing. Needed when the map is empty.
|
||||||
|
() => {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Returns the number of comma-separated expressions passed to it
|
||||||
|
#[macro_export]
|
||||||
|
#[doc(hidden)]
|
||||||
|
macro_rules! codelen {
|
||||||
|
() => { 0 };
|
||||||
|
( $one:expr ) => { 1 };
|
||||||
|
( $first:expr, $($rest:expr),+ ) => { 1 + codelen!($($rest),+) };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Replace elements of "arrays" of expressions with sorted replacements by
|
||||||
|
/// seeking along a "positional array" containing n expressions, then replacing
|
||||||
|
/// elements in the source array.
|
||||||
|
///
|
||||||
|
/// Expands to the first array with replacements applied. The length doesn't
|
||||||
|
/// change.
|
||||||
|
#[macro_export]
|
||||||
|
#[doc(hidden)]
|
||||||
|
macro_rules! lockstep_replace {
|
||||||
|
( [ $($result:expr),* ], [], ) => {
|
||||||
|
// `src` is empty, no relocations. We're done!
|
||||||
|
[ $($result),* ]
|
||||||
|
};
|
||||||
|
( [ $($result:expr),* ], [ $($src:expr,)+ ], ) => {
|
||||||
|
// Empty list of replacements, but still `src` to go
|
||||||
|
[ $($result,)* $($src),+ ]
|
||||||
|
};
|
||||||
|
( [ $($result:expr),* ], [ $($src:expr,)* ], [], [], $( [ $($pos:expr,)* ], [ $($rep:expr,)* ], )* ) => {
|
||||||
|
// All replacements applied. Pop the current replacement and continue.
|
||||||
|
lockstep_replace!(
|
||||||
|
[ $($result),* ],
|
||||||
|
[ $($src,)* ],
|
||||||
|
$(
|
||||||
|
[ $($pos,)* ],
|
||||||
|
[ $($rep,)* ],
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
};
|
||||||
|
( [ $($result:expr),* ], [ $src1_replaced:expr, $($src:expr,)* ], [], [ $rep1:expr, $($rep_rest:expr,)* ], $( [ $pos1:expr, $($pos:expr,)* ], [ $($rep:expr,)* ], )* ) => {
|
||||||
|
// Position of a replacement reached (or: inside a replacement)
|
||||||
|
// Coupled with a seek step
|
||||||
|
lockstep_replace!(
|
||||||
|
[ $($result,)* $rep1 ],
|
||||||
|
[ $($src,)* ],
|
||||||
|
[],
|
||||||
|
[ $($rep_rest,)* ],
|
||||||
|
$(
|
||||||
|
[ $($pos,)* ],
|
||||||
|
[ $($rep,)* ],
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
};
|
||||||
|
( [ $($result:expr),* ], [ $src1:expr, $($src:expr,)* ], $( [ $pos1:expr, $($pos:expr,)* ], [ $($rep:expr,)* ], )+ ) => {
|
||||||
|
// Seek to next replacement position (simultaneously for all
|
||||||
|
// replacements)
|
||||||
|
lockstep_replace!(
|
||||||
|
[ $($result,)* $src1 ],
|
||||||
|
[ $($src,)* ],
|
||||||
|
$(
|
||||||
|
[ $($pos,)* ],
|
||||||
|
[ $($rep,)* ],
|
||||||
|
)+
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs relocation of machine code based on given labels and relocations.
|
||||||
|
/// Looks up label names in an `ident_map`. Expands to the relocated machine
|
||||||
|
/// code.
|
||||||
|
///
|
||||||
|
/// Relocation formats:
|
||||||
|
/// { $label as ABS16 @ [$lockstepmcpos] }
|
||||||
|
|
||||||
|
// $pos is
|
||||||
|
#[macro_export]
|
||||||
|
#[doc(hidden)]
|
||||||
|
macro_rules! reloc {
|
||||||
|
( { $($attr:tt)* } [ $( [ $($pos:expr),* ], [ $($rep:expr),* ] ),* ], $lblmap:ident, [ $($mcode:expr),* ], [/* empty relocation list */] ) => {
|
||||||
|
lockstep_replace!([], [ $($mcode,)* ], $( [ $($pos,)* ], [ $($rep,)* ], )*)
|
||||||
|
};
|
||||||
|
( { start: $start:expr } [ $( [ $($pos:expr),* ], [ $($rep:expr),* ] ),* ], $lblmap:ident, [ $($mcode:expr),* ], [ { $lbl:ident as ABS16 @ [$($lockstepmcpos:expr),*] } $(,$reloc:tt)* ] ) => {
|
||||||
|
// Replace 2 Bytes with the absolute address
|
||||||
|
// Relocation position is given as "lock-step MC pos", an expression
|
||||||
|
// list that's as long as all mcode before the relocation should happen.
|
||||||
|
reloc!(
|
||||||
|
{ start: $start }
|
||||||
|
[ $( [ $($pos),* ], [ $($rep),* ] ,)*
|
||||||
|
[ $($lockstepmcpos),* ], [ ($lblmap!($lbl) + $start) as u8, (($lblmap!($lbl) + $start) >> 8) as u8 ] ],
|
||||||
|
$lblmap, [ $($mcode),* ], [ $($reloc),* ])
|
||||||
|
};
|
||||||
|
( { $($attr:tt)* } [ $( [ $($pos:expr),* ], [ $($rep:expr),* ] ),* ], $lblmap:ident, [ $($mcode:expr),* ], [ { $lbl:ident as PCREL @ [$($lockstepmcpos:expr),*] } $(,$reloc:tt)* ] ) => {
|
||||||
|
// Replace 1 Byte with the PC relative address
|
||||||
|
// PC is the program counter *after* the relocated offset (the length of the
|
||||||
|
// `$lockstepmcpos` array + 1), so we need to subtract 1 additional byte.
|
||||||
|
reloc!(
|
||||||
|
{ $($attr)* }
|
||||||
|
[ $( [ $($pos),* ], [ $($rep),* ] ,)*
|
||||||
|
[ $($lockstepmcpos),* ], [ ((( $lblmap!($lbl) as i32 - codelen!($($lockstepmcpos),*) as i32 - 1 ) & 0x3FF ) << 23) ] ],
|
||||||
|
$lblmap, [ $($mcode),* ], [ $($reloc),* ])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $attr is a label
|
||||||
|
// $mcode is a list that contains the machine code opcodes
|
||||||
|
// $ident is the identifier list
|
||||||
|
// $reloc is the relocation list
|
||||||
|
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
#[doc(hidden)]
|
||||||
|
macro_rules! asm_ {
|
||||||
|
( { $($attr:tt)* } [ $($mcode:expr),* ], [ $($lbl:ident => $lblval:expr),* ], [ $($reloc:tt),* ],
|
||||||
|
// EOF
|
||||||
|
) => {{
|
||||||
|
ident_map!(labelmap = {
|
||||||
|
$($lbl => $lblval),*
|
||||||
|
});
|
||||||
|
reloc!({ $($attr)* } [], labelmap, [ $($mcode),* ], [ $($reloc),* ])
|
||||||
|
}};
|
||||||
|
|
||||||
|
// ==================================================================================
|
||||||
|
// ==================================================================================
|
||||||
|
// ==================================================================================
|
||||||
|
|
||||||
|
// Opcode assembly table.
|
||||||
|
// Note that the weird order is required because macros try to match each arm in order, but
|
||||||
|
// don't backtrack when a NT is parsed
|
||||||
|
|
||||||
|
// ADD
|
||||||
|
( { $($attr:tt)* } [ $($mcode:expr),* ], [ $($lbl:ident => $lblval:expr),* ], [ $($reloc:tt),* ],
|
||||||
|
add % $wd:tt, % $ra:tt , % $rb:tt
|
||||||
|
$($rest:tt)* ) => {
|
||||||
|
asm_!({ $($attr)* } [ $($mcode,)* $wd << 16 | 0 << 11 | $ra << 6 | 0 << 17 | $rb << 12 | 0x5 ], [ $($lbl => $lblval),* ], [ $($reloc),* ], $($rest)*)
|
||||||
|
};
|
||||||
|
( { $($attr:tt)* } [ $($mcode:expr),* ], [ $($lbl:ident => $lblval:expr),* ], [ $($reloc:tt),* ],
|
||||||
|
add % $wd:tt, % $ra:tt , # $rb:tt
|
||||||
|
$($rest:tt)* ) => {
|
||||||
|
asm_!({ $($attr)* } [ $($mcode,)* $wd << 16 | 0 << 11 | $ra << 6 | 1 << 17 | $rb << 12 | 0x5 ], [ $($lbl => $lblval),* ], [ $($reloc),* ], $($rest)*)
|
||||||
|
};
|
||||||
|
( { $($attr:tt)* } [ $($mcode:expr),* ], [ $($lbl:ident => $lblval:expr),* ], [ $($reloc:tt),* ],
|
||||||
|
add % $wd:tt, # $ra:tt , % $rb:tt
|
||||||
|
$($rest:tt)* ) => {
|
||||||
|
asm_!({ $($attr)* } [ $($mcode,)* $wd << 16 | 1 << 11 | $ra << 6 | 0 << 17 | $rb << 12 | 0x5 ], [ $($lbl => $lblval),* ], [ $($reloc),* ], $($rest)*)
|
||||||
|
};
|
||||||
|
( { $($attr:tt)* } [ $($mcode:expr),* ], [ $($lbl:ident => $lblval:expr),* ], [ $($reloc:tt),* ],
|
||||||
|
add % $wd:tt, # $ra:tt , # $rb:tt
|
||||||
|
$($rest:tt)* ) => {
|
||||||
|
asm_!({ $($attr)* } [ $($mcode,)* $wd << 16 | 1 << 11 | $ra << 6 | 1 << 17 | $rb << 12 | 0x5 ], [ $($lbl => $lblval),* ], [ $($reloc),* ], $($rest)*)
|
||||||
|
};
|
||||||
|
|
||||||
|
// BRZ
|
||||||
|
( { $($attr:tt)* } [ $($mcode:expr),* ], [ $($lbl:ident => $lblval:expr),* ], [ $($reloc:tt),* ],
|
||||||
|
brz $label:ident , % $ra:tt
|
||||||
|
$($rest:tt)* ) => {
|
||||||
|
asm_!({ $($attr)* } [ $($mcode,)* 0x0 ],
|
||||||
|
[ $($lbl => $lblval),* ], [ $($reloc,)* { $label as PCREL @ [ $($mcode,)* ] } ], $($rest)*)
|
||||||
|
};
|
||||||
|
( { $($attr:tt)* } [ $($mcode:expr),* ], [ $($lbl:ident => $lblval:expr),* ], [ $($reloc:tt),* ],
|
||||||
|
brz $label:ident , # $ra:tt
|
||||||
|
$($rest:tt)* ) => {
|
||||||
|
asm_!({ $($attr)* } [ $($mcode,)* 0 << 23 | 1 << 11 | $ra << 6 | 0x9 ],
|
||||||
|
[ $($lbl => $lblval),* ], [ $($reloc,)* { $label as PCREL @ [$($mcode,)* 0 << 23 | 1 << 11 | $ra << 6 | 0x9] } ], $($rest)*)
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================================================================================
|
||||||
|
// ==================================================================================
|
||||||
|
// ==================================================================================
|
||||||
|
|
||||||
|
// Check for labels
|
||||||
|
( { $($attr:tt)* } [ $($mcode:expr),* ], [ $($lbl:ident => $lblval:expr),* ], [ $($reloc:tt),* ],
|
||||||
|
$label:ident :
|
||||||
|
$($rest:tt)* ) => {
|
||||||
|
asm_!(
|
||||||
|
{ $($attr)* }
|
||||||
|
[ $($mcode),* ],
|
||||||
|
[ $($lbl => $lblval,)* $label => codelen!($($mcode),*) ],
|
||||||
|
[ $($reloc),* ],
|
||||||
|
$($rest)*
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! assemble_engine25519 {
|
||||||
|
( {
|
||||||
|
start: $start:expr,
|
||||||
|
code: {
|
||||||
|
$($tokens:tt)*
|
||||||
|
}
|
||||||
|
} ) => {
|
||||||
|
asm_!({ start: $start } [], [], [], $($tokens)*)
|
||||||
|
};
|
||||||
|
( $($tokens:tt)* ) => {
|
||||||
|
assemble_engine25519!({
|
||||||
|
start: 0,
|
||||||
|
code: {
|
||||||
|
$($tokens)*
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
/// Does anything work
|
||||||
|
#[test]
|
||||||
|
fn basic_syntax() {
|
||||||
|
let mcode = assemble_engine25519!(
|
||||||
|
start:
|
||||||
|
add %0, %1, %2
|
||||||
|
add %2, %3, #4
|
||||||
|
add %5, #6, %7
|
||||||
|
add %8, #9, #10
|
||||||
|
brz start, %11
|
||||||
|
);
|
||||||
|
assert_eq!(mcode, [
|
||||||
|
0 << 16 | 0 << 11 | 1 << 6 | 0 << 17 | 2 << 12 | 0x5,
|
||||||
|
2 << 16 | 0 << 11 | 3 << 6 | 1 << 17 | 4 << 12 | 0x5,
|
||||||
|
5 << 16 | 1 << 11 | 6 << 6 | 0 << 17 | 7 << 12 | 0x5,
|
||||||
|
8 << 16 | 1 << 11 | 9 << 6 | 1 << 17 | 10 << 12 | 0x5,
|
||||||
|
0x3FC << 23 | 0 << 11 | 11 << 6 | 0x9,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Simple jump
|
||||||
|
#[test]
|
||||||
|
fn simple_jmp() {
|
||||||
|
let mcode = assemble_engine25519!(
|
||||||
|
start:
|
||||||
|
add %0, %1, %2
|
||||||
|
brz start, %11
|
||||||
|
);
|
||||||
|
print!("{}", mcode);
|
||||||
|
assert_eq!(mcode, [
|
||||||
|
0 << 16 | 0 << 11 | 1 << 6 | 0 << 17 | 2 << 12 | 0x5,
|
||||||
|
0x3FE << 23 | 0 << 11 | 11 << 6 | 0x9,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
/// Test simple label relocation
|
||||||
|
#[test]
|
||||||
|
fn simple_jmp() {
|
||||||
|
let mcode = assemble_engine25519!(
|
||||||
|
start: brz start, r0
|
||||||
|
);
|
||||||
|
assert_eq!(mcode, [ 0x4C, 0x00, 0x00 ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Has to work without any relocations (label references)
|
||||||
|
#[test]
|
||||||
|
fn no_reloc() {
|
||||||
|
let mcode = assemble6502!(
|
||||||
|
start:
|
||||||
|
lda #0xfb
|
||||||
|
);
|
||||||
|
assert_eq!(mcode, [ 0xA9, 0xFB ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Has to work without any labels
|
||||||
|
#[test]
|
||||||
|
fn no_label() {
|
||||||
|
let mcode = assemble6502!(
|
||||||
|
lda #0xfb
|
||||||
|
lda #0xab
|
||||||
|
);
|
||||||
|
assert_eq!(mcode, [ 0xA9, 0xFB, 0xA9, 0xAB ]);
|
||||||
|
}
|
||||||
|
*/
|
Loading…
Reference in New Issue
Block a user