2023-10-04 09:40:37 +00:00
use super ::* ;
/// Creates a [Parsable] implementation for an enum whose variants
/// are named after other [Parsable] items
macro make_parsable ( $( #[ $meta:meta ] ) * $vis :vis enum $id :ident { $( $( #[ $vmeta:meta ] ) * $v :ident ) , * $(, ) ? } ) {
$( #[ $meta ] ) * $vis enum $id { $( $( #[ $vmeta ] ) * $v ( $v ) , ) * }
impl ::msp430_asm ::parser ::parsable ::Parsable for $id {
fn parse < ' text , T > ( p : & Parser , stream : & mut T ) -> Result < Self , ParseError >
where T : TokenStream < ' text > {
$( if let Some ( v ) = Parsable ::try_parse ( p , stream ) ? { Ok ( Self ::$v ( v ) ) } else ) *
{ Err ( ParseError ::UnrecognizedDirective ( " " . into ( ) ) ) }
}
}
impl TryFrom < & str > for $id {
type Error = ParseError ;
fn try_from ( value : & str ) -> Result < Self , Self ::Error > {
Parsable ::parse ( & Parser ::default ( ) , & mut Tokenizer ::new ( value ) . ignore ( Type ::Space ) . preprocessed ( ) )
}
}
}
make_parsable! {
#[ derive(Debug) ]
pub enum SyntaxFragment {
Opcode ,
PrimaryOperand ,
Number ,
}
}
impl SyntaxFragment {
pub fn info ( & self ) {
match self {
SyntaxFragment ::Opcode ( o ) = > Self ::opcode_info ( o ) ,
SyntaxFragment ::PrimaryOperand ( o ) = > Self ::operand_info ( o ) ,
SyntaxFragment ::Number ( n ) = > println! ( " The number {n} " ) ,
}
}
fn opcode_info ( o : & Opcode ) {
let ( desc , as_rust ) = usage ( o ) ;
println! ( " Usage: {o} {} \n {desc} ( {as_rust} ) " , params ( o ) ) ;
footer! ( " https://mspgcc.sourceforge.net/manual/x223.html " ) ;
}
// TODO: re-enable full instruction decoding
// fn encoding_info(e: &Encoding) {
// match e {
// Encoding::Single { dst, .. } => Self::operand_info(dst),
// Encoding::Jump { target } => println!("Jumps to (pc + {target})"),
// Encoding::Double { src, dst, .. } => {
// Self::operand_info(src);
// Self::operand_info(&dst.clone().into())
// }
// }
// }
fn operand_info ( o : & PrimaryOperand ) {
match o {
PrimaryOperand ::Direct ( r ) = > Self ::register_info ( r ) ,
PrimaryOperand ::Indirect ( r ) = > {
Self ::register_info ( r ) ;
println! ( " Indirect addressing mode: use data pointed to by {r} " ) ;
}
PrimaryOperand ::PostInc ( r ) = > {
Self ::register_info ( r ) ;
println! ( " Indirect post-increment mode: use data pointed to by {r} , then increment {r} " ) ;
}
PrimaryOperand ::Indexed ( r , n ) = > {
Self ::register_info ( r ) ;
println! ( " Indexed mode: use the data at {r} [ {n} ] " ) ;
}
PrimaryOperand ::Relative ( _ ) = > return ,
PrimaryOperand ::Absolute ( n ) = > println! ( " Absolute mode: use the data at absolute address {n} " ) ,
PrimaryOperand ::Immediate ( n ) = > println! ( " Immediate mode: the constant {n} " ) ,
PrimaryOperand ::Four = > println! ( " #4 mode: Immediate 4 is encoded @sr " ) ,
PrimaryOperand ::Eight = > println! ( " #8 mode: Immediate 8 is encoded @sr+ " ) ,
PrimaryOperand ::Zero = > println! ( " #0 mode: Immediate 0 is encoded cg (r3) " ) ,
PrimaryOperand ::One = > println! ( " #1 mode: Immediate 1 is encoded _(cg), where _ is a nonexistent ext-word " ) ,
PrimaryOperand ::Two = > println! ( " #2 mode: Immediate 2 is encoded @cg " ) ,
PrimaryOperand ::MinusOne = > println! ( " #-1 mode: the all-ones constant, is encoded @cg+ " ) ,
}
footer! ( " https://mspgcc.sourceforge.net/manual/x82.html " ) ;
}
fn register_info ( r : & Register ) {
use Register as Re ;
match r {
Re ::pc = > println! ( " pc (r0) is the Program Counter. Post-increment addressing will increase it by 2. " ) ,
Re ::sp = > println! ( " sp (r1) is the Stack Pointer. Post-increment addressing will increase it by 2. " ) ,
Re ::sr = > println! (
" sr (r2) is the Status Register. It has arithmetic flags: oVerflow, Negative, Zero, and Carry; \n Interrupt Enable; and toggles for various clock/sleep functions. \n 8 \t 7 \t 6 \t 5 \t 4 \t 3 \t 2 \t 1 \t 0 \n V \t SCG1 \t SCG1 \t OSCOFF \t CPUOFF \t GIE \t N \t Z \t C " ,
) ,
Re ::cg = > println! ( " cg (r3) is the Constant Generator. It's hard-wired to zero. " ) ,
Re ::r4 | Re ::r5 | Re ::r6 | Re ::r7 | Re ::r8 | Re ::r9 | Re ::r10 | Re ::r11 = > {
println! ( " {r} is a callee-saved general purpose register. " )
}
Re ::r12 | Re ::r13 | Re ::r14 | Re ::r15 = > {
println! ( " {r} is a caller-saved general purpose register, allowed for return values. " )
}
}
}
}
// Gets parameter usage information from the opcode's EncodingParser
pub fn params ( opcode : & Opcode ) -> & 'static str {
match opcode . resolve ( ) . 1 {
EncodingParser ::Jump { target : None } = > " target (relative address or label) " ,
EncodingParser ::Single { width : None , dst : None } = > " [.b] dst " ,
EncodingParser ::Single { dst : None , .. } = > " dst " ,
EncodingParser ::Double { src : None , dst : None , .. } = > " [.b] src, dst " ,
EncodingParser ::Double { src : None , .. } = > " [.b] src " ,
EncodingParser ::Double { dst : None , .. } = > " [.b] dst " ,
EncodingParser ::Double { .. } = > " [.b] " ,
EncodingParser ::Reflexive { reg : None , .. } = > " [.b] dst " ,
_ = > " " ,
}
}
pub fn usage ( opcode : & Opcode ) -> ( & 'static str , & 'static str ) {
match opcode {
// Single
Opcode ::Rrc = > ( " Rotates dst right, through carry flag " , " dst = (dst >> 1) | (sr[C] << 15) " ) ,
Opcode ::Swpb = > ( " Swaps the high and low byte of dst " , " dst.swap_bytes() " ) ,
Opcode ::Rra = > ( " Shifts dst right, sign-extending the result " , " dst >>= 1 " ) ,
Opcode ::Sxt = > ( " Sign-extends the 8-bit dst to 16-bits " , " dst as i16 << 8 >> 8 " ) ,
Opcode ::Push = > ( " Pushes dst to the stack " , " stack.push(dst) " ) ,
Opcode ::Call = > ( " Calls a subroutine at an absolute address " , " dst() " ) ,
Opcode ::Reti = > ( " Return from interrupt handler " , " { sr = stack.pop(); pc = stack.pop() } " ) ,
// Jump
Opcode ::Jnz = > ( " Jump if the last result was not zero " , " if !Z { pc += target } " ) ,
Opcode ::Jz = > ( " Jump if the last result was zero " , " if Z { pc += target } " ) ,
Opcode ::Jnc = > ( " Jump if the last operation did not carry " , " if !C { pc += target } " ) ,
Opcode ::Jc = > ( " Jump if the last operation produced a carry bit " , " if C { pc += target } " ) ,
Opcode ::Jn = > ( " Jump if the last result was negative " , " if N { pc += target } " ) ,
Opcode ::Jge = > ( " Jump if the flags indicate src >= dst " , " if sr[C] == sr[V] { pc += target } " ) ,
Opcode ::Jl = > ( " Jump if the flags indicate src < dst " , " if sr[C] != sr[V] { pc += target } " ) ,
Opcode ::Jmp = > ( " Jump unconditionally " , " pc += target " ) ,
// Double
Opcode ::Mov = > ( " Copy src into dst " , " dst = src " ) ,
Opcode ::Add = > ( " Add src to dst " , " dst += src " ) ,
Opcode ::Addc = > ( " Add src to dst with carry " , " dst += src + sr[C] " ) ,
Opcode ::Subc = > ( " Subtract src from dst with carry " , " dst -= src - sr[C] " ) ,
Opcode ::Sub = > ( " Subtract src from dst " , " dst -= src " ) ,
Opcode ::Cmp = > ( " Subtract src from dst, but discard the result, keeping the flags " , " dst - src " ) ,
Opcode ::Dadd = > ( " Add src to dst in Binary Coded Decimal " , " dst = dst as BCD + src as BCD " ) ,
Opcode ::Bit = > ( " Test if bits in src are set in dst " , " (src & dst).cmp(0) " ) ,
Opcode ::Bic = > ( " Clear bits in dst that are set in src, without changing flags " , " dst &= !src " ) ,
Opcode ::Bis = > ( " Set bits in dst that are set in src, without changing flags " , " dst |= src " ) ,
Opcode ::Xor = > ( " Bitwise Xor src into dst " , " dst ^= src " ) ,
Opcode ::And = > ( " Bitwise And src into dst " , " dst &= src " ) ,
// Emulated
Opcode ::Nop = > ( " Does nothing " , " {} " ) ,
Opcode ::Pop = > ( " Pops a value from the stack " , " dst = stack.pop() " ) ,
Opcode ::Br = > ( " Branches to the absolute address in src " , " pc = src " ) ,
Opcode ::Ret = > ( " Returns from subroutine " , " pc = stack.pop() " ) ,
Opcode ::Clrc = > ( " Clears the carry flag " , " sr[C] = 0 " ) ,
Opcode ::Setc = > ( " Sets the carry flag " , " sr[C] = 1 " ) ,
Opcode ::Clrz = > ( " Clears the zero flag " , " sr[Z] = 0 " ) ,
Opcode ::Setz = > ( " Sets the zero flag " , " sr[Z] = 1 " ) ,
Opcode ::Clrn = > ( " Clears the negative flag " , " sr[N] = 0 " ) ,
Opcode ::Setn = > ( " Sets the negative flag " , " sr[N] = 1 " ) ,
Opcode ::Dint = > ( " Disables interrupts " , " sr[GIE] = 0 " ) ,
Opcode ::Eint = > ( " Enables interrupts " , " sr[GIE] = 1 " ) ,
Opcode ::Rla = > ( " Shifts dst to the left, padding with zeros " , " dst <<= 1 " ) ,
Opcode ::Rlc = > ( " Rotates dst to the left, through carry flag " , " dst = (dst << 1) + sr[C] " ) ,
Opcode ::Inv = > ( " Inverts the bits in dst " , " dst = !dst " ) ,
Opcode ::Clr = > ( " Sets dst to 0 " , " dst = 0 " ) ,
Opcode ::Tst = > ( " Sets the status register flags (CNZV) using dst " , " " ) ,
Opcode ::Dec = > ( " Decrements dst " , " dst -= 1 " ) ,
Opcode ::Decd = > ( " Decrements dst by 2 (one processor word) " , " dst -= 2 " ) ,
Opcode ::Inc = > ( " Increments dst " , " dst += 1 " ) ,
Opcode ::Incd = > ( " Increments dst by 2 (one processor word) " , " dst += 2 " ) ,
Opcode ::Adc = > ( " Adds the carry bit to dst " , " dst += sr[C] " ) ,
Opcode ::Dadc = > ( " Adds the carry bit to dst, in Binary Coded Decimal " , " dst as BCD = sr[C] " ) ,
Opcode ::Sbc = > ( " Subtracts the carry bit from dst " , " dst -= sr[C] " ) ,
}
}
pub fn list_opcodes ( ) {
header! ( " Single-operand instructions: " ) ;
println! ( " rrc \t swpb \t rra \t sxt \t push \t call \n reti " ) ;
header! ( " Relative Jump instructions: " ) ;
println! ( " jnz \t jz \t jnc \t jc \t jn \t jge \n jl \t jmp " ) ;
header! ( " Double-operand instructions: " ) ;
println! ( " mov \t add \t addc \t subc \t sub \t cmp \n dadd \t bit \t bic \t bis \t xor \t and " ) ;
header! ( " Simulated instructions: " ) ;
println! ( " nop \t pop \t br \t ret \t clrc \t setc \n clrz \t setz \t clrn \t setn \t dint \t eint " ) ;
println! ( " rla \t rlc \t inv \t clr \t tst \t dec \n decd \t inc \t incd \t adc \t dadc \t sbc " ) ;
}
macro header ( $( $x : expr ) , + ) {
{ print! ( " {} " , SetForegroundColor ( Color ::Cyan ) ) ; print! ( $( $x ) , + ) ; println! ( " {} " , ResetAttributes ) ; }
}
macro footer ( $( $x : expr ) , + ) {
2023-10-04 09:44:26 +00:00
{ print! ( " {} " , SetForegroundColor ( Color ::DarkGray ) ) ; print! ( $( $x ) , + ) ; println! ( " {} " , ResetAttributes ) ; }
2023-10-04 09:40:37 +00:00
}