initial commit
This commit is contained in:
		
							
								
								
									
										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 ]); | ||||
| } | ||||
| */ | ||||
		Reference in New Issue
	
	Block a user