ghidra/Ghidra/Processors/Atmel/data/languages/avr8.sinc

1201 lines
35 KiB
Plaintext

# sleigh specification for the avr8
#
# Currently designed for ATMega64 in non-ATmel103 configuration
# - 0x20-0xff as IO memory, rather than 0x20-0x5f
#
# This is a ATMega64 with a 64k sized memory
# Other parts available have a 4M sized memory so that stack
# pointer would be three bytes instead
define endian=little;
# Declaring space to be word-sized... alternative is to do byte sized
define alignment=2;
# Force fusion of two byte operations in a row by decoding as words
#@define FUSION ""
#define where the IO space is mapped if not specified
@ifndef IO_START
@define IO_START "0x20"
@define EIND "0x5c"
@endif
#define where the registers are located if not specified
@ifndef REGISTER_SPACE
@define REGISTER_SPACE "mem"
@endif
# mem space should really be the default, but the loading scripts will
# prefer the code space as the default. By being explicit for every
# instruction, we can eliminate the ambiguity for at least the
# decompiler. None-the-less, other than when loading the binary into
# Ghidra, it's still preferable to see the name of IO locations used,
# rather than code addresses, so leave mem space as the default.
define space code type=ram_space size=$(PCBYTESIZE) wordsize=2 default;
define space register type=register_space size=2;
define space mem type=ram_space size=2 wordsize=1;
# Using decimal rather than hex to match specs
# General registers start at 0 in the iospace for earlier avr8 processors
# In the Xmega line, they are not accessible in mem, and are in a register space
#
define $(REGISTER_SPACE) offset=0 size=1 [
R0 R1 R2 R3 R4 R5 R6 R7 R8 R9
R10 R11 R12 R13 R14 R15 R16 R17 R18 R19
R20 R21 R22 R23 Wlo Whi Xlo Xhi Ylo Yhi
Zlo Zhi
];
define $(REGISTER_SPACE) offset=0 size=2 [
R1R0 R3R2 R5R4 R7R6 R9R8
R11R10 R13R12 R15R14 R17R16 R19R18
R21R20 R23R22 W # Technically, manual has R25R24 instead of W.
X Y Z
];
define $(REGISTER_SPACE) offset=0x10 size=4 [
R19R18R17R16 R23R22R21R20
];
# Technically, the stack pointer is in the i/o space so should be addressable with the
# rest of the i/o registers. However, Ghidra does not react well to the stack pointer
# being indirectly addressable so we're making an exception.
define register offset=0x3D size=1 [ SPL SPH ];
define register offset=0x3D size=2 [ SP ];
define register offset=0x42 size=$(PCBYTESIZE) [ PC ];
define register offset=0x80 size=1 [
Cflg Zflg Nflg Vflg Sflg Hflg Tflg Iflg SKIP
];
#####################################
# Some AVR processors may have different io layouts not just different io.
#
# AVR processors with more than 64 KiB of RAM make use of the RAMP- registers
# to act as the high bits where the X, Y, or Z registers are used, or in direct
# addressing instructions.
#
# TODO: incorporate the use of RAMPX, RAMPY, RAMPZ in the LD, ST instructions
# ELPM, and LDS instructions use RAMPZ and RAMPD
#
# These IO registers need to be accessible to sleigh instruction PCODE
# so they are defined here. The bulk of the IO registers are defined
# as labels in the appropriate .pspec file.
define mem offset=$(IO_START) size = 1 [
# IO_START + 0x00
_ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _
# IO_START + 0x10
_ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _
# IO_START + 0x20
_ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _
# IO_START + 0x30
_ _ _ _ _ _ _ _
RAMPD RAMPX RAMPY RAMPZ _ _ _ SREG
];
# If the AVR processor has more than 128 KiB of ROM, the processor will support the EIND
# register along with the EIJMP and EICALL extended instructions.
@if HASEIND == "1"
define mem offset=$(EIND) size=1 [ EIND ];
@endif
##############################
# Define context bits
define register offset=0x90 size=4 contextreg;
define context contextreg
useSkipCond = (0,0) noflow # =1 skip instruction if SKIP register is true
# transient context
phase = (1,1) # =0 check for useSkipCond, =1 parse instruction
;
## Following 8051 example rather than define bitrange
# Works better as distinct variables
@define Cflag "Cflg"
@define Zflag "Zflg"
@define Nflag "Nflg"
@define Vflag "Vflg"
@define Sflag "Sflg"
@define Hflag "Hflg"
@define Tflag "Tflg"
@define Iflag "Iflg"
define token opword (16)
ophi16 = ( 0,15)
ophi9 = ( 7,15)
ophi8 = ( 8,15)
ophi7 = ( 9,15)
ophi6 = (10,15)
ophi5 = (11,15)
ophi4 = (12,15)
ophi2 = (14,15)
opbit13 = (13,13)
opbit12 = (12,12)
opbit9 = ( 9, 9)
opbit8 = ( 8, 8)
opbit7 = ( 7, 7)
opbit3 = ( 3, 3)
opbit2 = ( 2, 2)
opbit0 = ( 0, 0)
oplow12 = ( 0,11)
oplow12signed = ( 0,11) signed
oplow4 = ( 0, 3)
oplow3_flag = ( 0, 2)
oplow3 = ( 0, 2)
oplow2 = ( 0, 1)
op1to3 = ( 1, 3)
op2to3 = ( 2, 3)
op3to7 = ( 3, 7)
op4to8 = ( 4, 8)
op4to6 = ( 4, 6)
op4to6_flag = ( 4, 6)
op6to7 = ( 6, 7)
op9to10 = ( 9,10)
op10to11 = (10,11)
RdHi = ( 4, 7)
RdHi3 = ( 4, 6)
RdFull = ( 4, 8)
RrHi = ( 0, 3)
RrHi3 = ( 0, 2)
RrLow = ( 0, 3)
RrHiLowSel = ( 9, 9)
Rdw2 = ( 4, 5)
Rdw4 = ( 4, 7)
Rrw4 = ( 0, 3)
Rstq = ( 3, 3)
RstPtr = ( 2, 3)
op0to3 = ( 0, 3)
op3to9signed = ( 3, 9) signed
op4to7 = ( 4, 7)
op8to11 = ( 8,11)
;
define token immtok(16)
next16 = (0,15)
;
define token opfusion16(32)
op1hi4 = (12,15)
op2hi4 = (28,31)
op1hi6 = (10,15)
op2hi6 = (26,31)
op1low4 = ( 0, 3)
op2low4 = (16,19)
op1bits0to3 = ( 0, 3)
op2bits0to3 = (16,19)
op1bits1to3 = ( 1, 3)
op2bits1to3 = (17,19)
op1bits4to8 = ( 4, 8)
op2bits4to8 = (20,24)
op1bits5to7 = ( 5, 7)
op2bits5to7 = (21,23)
op1bits5to8 = ( 5, 8)
op2bits5to8 = (21,24)
op1bits8to11 = ( 8,11)
op2bits8to11 = (24,27)
op1bit0 = ( 0, 0)
op2bit0 = (16,16)
op1bit4 = ( 4, 4)
op2bit4 = (20,20)
op1bit9 = ( 9, 9)
op2bit9 = (25,25)
op1RdPair = ( 5, 8)
op1RdPairHi = ( 5, 7)
op1RrPairLow = ( 1, 3)
op1RrPairHi = ( 1, 3)
op1RrPairSel = ( 9, 9)
;
define token opfusion24(48)
f3op1hi4 = (12,15)
f3op2hi4 = (28,31)
f3op3hi4 = (34,47)
f3op1hi6 = (10,15)
f3op2hi6 = (26,31)
f3op3hi6 = (42,47)
f3op1bits0to3 = ( 0, 3)
f3op2bits0to3 = (16,19)
f3op3bits0to3 = (32,35)
f3op2bits4to7 = (20,23)
f3op1bits5to7 = ( 5, 7)
f3op3bits5to7 = (37,39)
f3op1bits8to11 = ( 8,11)
f3op2bits8to11 = (24,27)
f3op1bit4 = ( 4, 4)
f3op3bit4 = (36,36)
f3op3bit8 = (40,40)
f3op3bit9 = (41,41)
f3op1RdPairHi = ( 5, 7)
f3op2RdHi = (20,23)
;
define token opfusionLdsw(64) # lds lds
ldswop1hi7 = ( 9,15)
ldswop2hi7 = (41,47)
ldswop1low4 = ( 0, 3)
ldswop2low4 = (32,35)
ldswop1bits5to8 = ( 5, 8)
ldswop2bits5to8 = (37,40)
ldswop1bit4 = ( 4, 4)
ldswop2bit4 = (36,36)
ldswop1bit16 = (16,16)
ldswop2bit16 = (48,48)
ldswop1imm15 = (17,31)
ldswop2imm15 = (49,63)
ldswop1imm6 = (17,22)
ldswop2imm6 = (49,54)
ldswop1imm16 = (16,31)
ldswop2imm16 = (48,63)
ldswop1RdPair = ( 5, 8)
stswop2RdPair = (37,40)
;
attach variables [ oplow3_flag op4to6_flag ] [
Cflg Zflg Nflg Vflg Sflg Hflg Tflg Iflg
];
attach variables [ RdHi RrHi f3op2RdHi ] [
R16 R17 R18 R19
R20 R21 R22 R23 Wlo Whi Xlo Xhi Ylo Yhi
Zlo Zhi ]
;
attach variables [ RdHi3 RrHi3 ] [
R16 R17 R18 R19
R20 R21 R22 R23
];
attach variables [ RrLow ] [
R0 R1 R2 R3 R4 R5 R6 R7 R8 R9
R10 R11 R12 R13 R14 R15
];
attach variables [ RdFull ] [
R0 R1 R2 R3 R4 R5 R6 R7 R8 R9
R10 R11 R12 R13 R14 R15
R16 R17 R18 R19
R20 R21 R22 R23 Wlo Whi Xlo Xhi Ylo Yhi
Zlo Zhi
];
attach variables [ Rdw2 ] [
W X Y Z
];
attach variables [ Rstq ] [
Z Y
];
attach variables [ RstPtr ] [
Z _ Y X
];
attach variables [ Rdw4 Rrw4 op1RdPair ldswop1RdPair stswop2RdPair ] [
R1R0 R3R2 R5R4 R7R6 R9R8
R11R10 R13R12 R15R14 R17R16 R19R18
R21R20 R23R22 W
X Y Z
];
attach variables [ op1RrPairLow ] [
R1R0 R3R2 R5R4 R7R6 R9R8
R11R10 R13R12 R15R14
];
attach variables [ op1RrPairHi op1RdPairHi f3op1RdPairHi ] [
R17R16 R19R18
R21R20 R23R22 W
X Y Z
];
RrFull: RrHi is RrHiLowSel=1 & RrHi { tmp:1 = RrHi; export tmp; }
RrFull: RrLow is RrHiLowSel=0 & RrLow { tmp:1 = RrLow; export tmp; }
# Alternative: try using some subcontructors
op1RrPair: op1RrPairHi is op1RrPairSel=1 & op1RrPairHi { tmp:2 = op1RrPairHi; export tmp; }
op1RrPair: op1RrPairLow is op1RrPairSel=0 & op1RrPairLow { tmp:2 = op1RrPairLow; export tmp; }
# I'm uneasy at these... as they require the top of the stack
# to know what size element to reserve before the push.
# The docs should probably say that the top of the stack byte is unused...
#
# The processor is post-decremented, and because of the way the compiler
# manipulates the stack pointer it's important to get this correct.
@if PCBYTESIZE == "2"
macro pushPC(val) {
local valb:1 = val(0);
*[mem]:1 SP = valb;
SP = SP - 1;
valb = val(1);
*[mem]:1 SP = valb;
SP = SP - 1;
}
macro popPC(val) {
local valb:1 = 0;
SP = SP + 1;
val = *[mem]:1 SP;
SP = SP + 1;
valb = *[mem]:1 SP;
val = (val << 8) + zext(valb);
}
@else # PCBYTESIZE == 3
macro pushPC(val) {
local valb:1 = val(0);
*[mem]:1 SP = valb;
SP = SP - 1;
valb = val(1);
*[mem]:1 SP = valb;
SP = SP - 1;
valb = val(2);
*[mem]:1 SP = valb;
SP = SP - 1;
}
macro popPC(val) {
SP = SP + 1;
val = *[mem]:1 SP;
SP = SP + 1;
local valb = *[mem]:1 SP;
val = (val << 8) + zext(valb);
SP = SP + 1;
valb = *[mem]:1 SP;
val = (val << 8) + zext(valb);
}
@endif
macro push8(val) {
*[mem]:1 SP = val;
SP = SP -1;
}
macro pop8(val) {
SP = SP + 1;
val = *[mem]:1 SP;
}
# .slaspec shortcoming: Hflag isn't computed for most results
macro setSflag() {
$(Sflag) = $(Nflag) ^ $(Vflag);
}
macro setResultFlags(result) {
$(Nflag) = (result s< 0);
$(Zflag) = (result == 0x0);
setSflag();
}
macro doSubtract(pre,sub,res) {
local x = pre - sub;
$(Vflag) = sborrow(pre,sub);
$(Cflag) = (pre < sub);
setResultFlags(x);
$(Sflag) = pre s< sub;
res = x;
}
macro doSubtractWithCarry(pre,sub,res) {
local partial = pre - sub;
local subCarry = sub + $(Cflag);
local x = pre - subCarry;
local oldZflag = $(Zflag);
$(Vflag) = sborrow(pre,sub) ^^ sborrow(partial, $(Cflag));
$(Cflag) = (pre < sub) || (partial < $(Cflag));
setResultFlags(x);
$(Sflag) = $(Nflag)^$(Vflag);
$(Zflag) = oldZflag & $(Zflag);
res = x;
}
macro setMulFlags(res) {
$(Cflag) = ((res & 0x8000) != 0);
$(Zflag) = (res == 0);
}
macro loadSREG(reg) {
reg = (zext(Cflg==1) << 0) | (zext(Zflg==1) << 1) | (zext(Nflg==1) << 2) | (zext(Vflg==1) << 3) | (zext(Sflg==1) << 4) | (zext(Hflg==1) << 5) | (zext(Tflg==1)<<6) | (zext(Iflg==1) << 7);
SREG = reg;
}
macro storeSREG(val) {
Cflg = ((val>> 0) & 1);
Zflg = ((val>> 1) & 1);
Nflg = ((val>> 2) & 1);
Vflg = ((val>> 3) & 1);
Sflg = ((val>> 4) & 1);
Hflg = ((val>> 5) & 1);
Tflg = ((val>> 6) & 1);
Iflg = ((val>> 7) & 1);
SREG = val;
}
# Handle possible skip instruction
# This next line is a NOP except for the phase, which is never really checked.
# A better fix may be to use -l, and ensure phase=1 is checked on the base constructors.
:^instruction is phase=0 & useSkipCond=0 & instruction [ phase=1; ] { build instruction; }
:^instruction is phase=0 & useSkipCond=1 & instruction [ phase=1; ] {
if (SKIP) goto inst_next;
build instruction;
}
# K8 is immediate for Rd,K8 forms
K8: val is op0to3 & op8to11 [ val = (op8to11 << 4) | op0to3; ] { tmp:1 = val; export tmp; }
@ifdef FUSION
K16fuse: val is op1bits0to3 & op1bits8to11 & op2bits0to3 & op2bits8to11 [ val = (((op2bits8to11 << 4) | op2bits0to3) << 8) | ((op1bits8to11 << 4) | op1bits0to3); ] { tmp:2 = val; export tmp; }
f3cmpK16: val is f3op1bits0to3 & f3op1bits8to11 & f3op2bits0to3 & f3op2bits8to11 [ val = (((f3op2bits8to11 << 4) | f3op2bits0to3) << 8) | ((f3op1bits8to11 << 4) | f3op1bits0to3); ] { tmp:2 = val; export tmp; }
f3cmpK8: val is f3op2bits0to3 & f3op2bits8to11 [ val = (f3op2bits8to11 << 4) | f3op2bits0to3; ] { tmp:1 = val; export tmp; }
@endif
rel7addr: rel is op3to9signed [ rel = (op3to9signed + inst_next);] {
export *[code]:2 rel;
}
rel7dst: byteOffset is op3to9signed & rel7addr [ byteOffset = (op3to9signed + inst_next) << 1;] {
export rel7addr;
}
rel12addr: rel is oplow12signed [ rel = oplow12signed + inst_start + 1; ] {
export *[code]:2 rel;
}
rel12dst: byteOffset is oplow12signed & rel12addr [ byteOffset = (oplow12signed + inst_start + 1) << 1; ] {
export rel12addr;
}
abs22addr: loc is op4to8 & opbit0; next16 [ loc = (op4to8 << 17) | (opbit0 << 16) | next16; ] {
export *[code]:2 loc;
}
abs22dst: byteOffset is (op4to8 & opbit0; next16) & abs22addr [ byteOffset = ((op4to8 << 17) | (opbit0 << 16) | next16) << 1; ] {
export abs22addr;
}
next16memPtrVal1: next16 is next16 { export *[mem]:1 next16; }
@if PCBYTESIZE == "3"
next24memPtrVal1: next24 is next16 [next24 = (RAMPD << 16) | next16;] { export *[mem]:1 next24; }
@endif
@ifdef FUSION
ldswMemPtrVal2: ldswop1imm16 is ldswop1imm16 { export *[mem]:2 ldswop1imm16; }
stswMemPtrVal2: ldswop2imm16 is ldswop2imm16 { export *[mem]:2 ldswop2imm16; }
@endif
# K6 is used in dword operation
K6: val is oplow4 & op6to7 [ val = (op6to7 << 4) | oplow4; ] { tmp:1 = val; export tmp; }
# K7 is used by lds
K7addr: val is oplow4 & op9to10 & opbit8 [ val = ((1 ^ opbit8) << 7) | (opbit8 << 6) | (op9to10 << 4) | oplow4; ] {
tmp:1 = val; export tmp;
}
# Join against various spaces for dataspace...
# #####################################################################################
# COMMENTING OUT BECAUSE "subtable symbol K7addr is not allowed in context block"
#K7Ioaddr: val is K7addr [ val = K7addr - 0x20; ] { tmp:1 = val; export tmp; }
# #####################################################################################
# COMMENTING OUT BECAUSE "Subtable symbol K7Ioaddr is not allowed in context block"
#A7Ioaddr: val is K7Ioaddr [ val = (K7Ioaddr | 0x00) + 0x20 ; ] { export *[mem]:1 val; }
Aio6: val is oplow4 & op9to10 [ val = ((op9to10 << 4) | oplow4) + $(IO_START); ] { export *[mem]:1 val; }
Aio5: val is op3to7 [ val = (op3to7 | 0x00) + $(IO_START); ] { export *[mem]:1 val; }
q6: val is oplow3 & op10to11 & opbit13 [ val = (opbit13 << 5) | (op10to11 << 3) | oplow3; ] { tmp:1 = val; export tmp; }
@ifdef FUSION
# Predicates to verify that fusion will be valid here.
# We just want to construct these. The rules are not null to avoid a NOP bug with sleigh
fusion16rrrrPred: val is op1bit0=0 & op2bit0=1 & op1bit4=0 & op2bit4=1 & op1bit9=op2bit9 & op1bits5to8=op2bits5to8 & op1bits1to3=op2bits1to3 [ val = 0; ] { tmp:2=val; export tmp; }
fusion16rkrkPred: val is op1bits5to7=op2bits5to7 & op1bit4=0 & op2bit4=1 [ val=0; ] { tmp:2 = val; export tmp; }
f3cmpPairPred: val is f3op1bits5to7=f3op3bits5to7 & f3op1bit4=0 & f3op3bit4=1 & f3op3bit8=1 [ val=0; ] { tmp:2 = val; export tmp; }
f3cmpLdiPred: val is f3op3bit9=1 & f3op3bits0to3=f3op2bits4to7 [ val=0; ] { tmp:2 = val; export tmp; }
ldswPairPred: val is ldswop1bit4=0 & ldswop2bit4=1 & ldswop1bits5to8=ldswop2bits5to8 [ val=0; ] { tmp:2 = val; export tmp; }
stswPairPred: val is ldswop1bit4=1 & ldswop2bit4=0 & ldswop1bits5to8=ldswop2bits5to8 [ val=0; ] { tmp:2 = val; export tmp; }
# would like to check this for const pair, but hangs sleigh compiler: ldswop1imm15=ldswop2imm15
# So check as a few in a row
# Not any better & ldswop1imm5b=ldswop2imm5b & ldswop1imm5c=ldswop2imm5c
ldswConstPairPred: val is ldswop1bit16=0 & ldswop2bit16=1 & ldswop1imm6=ldswop2imm6 [ val=0; ] { tmp:2 = val; export tmp; }
stswConstPairPred: val is ldswop1bit16=1 & ldswop2bit16=0 & ldswop1imm6=ldswop2imm6 [ val=0; ] { tmp:2 = val; export tmp; }
@endif
define pcodeop todo;
define pcodeop todoflow;
define pcodeop todoflags;
define pcodeop todotst;
define pcodeop break;
@ifdef FUSION
# add followed by adc
:addw op1RdPair,op1RrPair is phase=1 & op1hi6=0x3 & op2hi6=0x7 & op1RdPair & op1RrPair & fusion16rrrrPred {
local pre = op1RdPair;
local post = op1RdPair + op1RrPair;
$(Cflag) = carry(op1RdPair,op1RrPair);
$(Vflag) = scarry(pre,op1RrPair);
op1RdPair = post;
setResultFlags(post);
}
@endif
# Rd,Rr
:adc RdFull,RrFull is phase=1 & ophi6=0x7 & RdFull & RrFull {
local res = RdFull + RrFull + $(Cflag);
$(Cflag) = carry(RdFull, RrFull) || carry(RdFull + RrFull, $(Cflag));
$(Vflag) = scarry(RdFull, RrFull) ^^ scarry(RdFull + RrFull, $(Cflag));
setResultFlags(res);
RdFull = res;
}
# Rd,Rr
:add RdFull,RrFull is phase=1 & ophi6=0x3 & RdFull & RrFull {
local res = RdFull + RrFull;
$(Cflag) = carry(RdFull,RrFull);
$(Vflag) = scarry(RdFull,RrFull);
setResultFlags(res);
RdFull = res;
}
# adiw Rd+1:Rd,K6
:adiw Rdw2,K6 is phase=1 & ophi8=0x96 & Rdw2 & K6 {
local pre = Rdw2;
Rdw2 = Rdw2 + zext(K6);
$(Cflag) = carry(pre,zext(K6));
$(Vflag) = scarry(Rdw2,zext(K6));
setResultFlags(Rdw2);
}
# and Rd,Rr
:and RdFull,RrFull is phase=1 & ophi6=8 & RdFull & RrFull {
RdFull = RdFull & RrFull;
$(Vflag) = 0;
setResultFlags(RdFull);
}
# andi Rd,K
:andi RdHi,K8 is phase=1 & ophi4=7 & RdHi & K8 {
RdHi = RdHi & K8;
$(Vflag) = 0;
setResultFlags(RdHi);
}
# asr Rd
:asr RdFull is phase=1 & ophi7=0x4a & oplow4=0x5 & RdFull { #done
$(Cflag) = RdFull & 0x01;
RdFull = RdFull s>> 1;
$(Nflag) = (RdFull & 0x80) == 0x80;
$(Vflag) = $(Nflag) ^ $(Cflag);
setResultFlags(RdFull);
}
# bclr s
:bclr op4to6_flag is phase=1 & ophi9=0x129 & oplow4=0x4 & op4to6_flag { #done
op4to6_flag = 0;
}
# bld Rd,b
:bld RdFull,oplow3 is phase=1 & ophi7=0x7c & opbit3=0 & RdFull & oplow3 {
local b = $(Tflag) << oplow3;
local mask = 0xff ^ (1 << oplow3);
RdFull = (RdFull & mask) | b;
}
# brbc s,k
:brbc rel7dst,oplow3_flag is phase=1 & ophi6=0x3d & rel7dst & oplow3_flag {
if (!oplow3_flag)
goto rel7dst;
}
# brbs s,k (see prev instruction)
:brbs rel7dst,oplow3_flag is phase=1 & ophi6=0x3c & rel7dst & oplow3_flag {
if (oplow3_flag)
goto rel7dst;
}
# brcs and brcc seem to be special cases of brbs
:break is phase=1 & ophi16=0x9598 {
break();
}
# Probably want to check for various decode logic for conditional branches...
# ... specifically BRBS 1,k
# breq k - really is BRBS 1,k
# bset s
:bset op4to6_flag is phase=1 & ophi9=(0x94<<1) & oplow4=0x8 & op4to6_flag {
op4to6_flag = 1;
}
# bst Rd,b
:bst RdFull,oplow3 is phase=1 & ophi7=0x7d & opbit3=0 & RdFull & oplow3 {
$(Tflag) = (RdFull >> oplow3) & 0x01;
}
# call k - todo - handle upper bits for 24 bit architecture
:call abs22dst is phase=1 & (ophi7=0x4a & op1to3=0x7) ... & abs22dst {
tmp:$(PCBYTESIZE) = inst_next >> 1;
pushPC(tmp);
PC = &abs22dst;
call abs22dst;
}
# cbi A,b
:cbi Aio5,oplow3 is phase=1 & ophi8=0x98 & Aio5 & oplow3 {
local x = Aio5;
x = x & (0xff ^ (1 << oplow3));
Aio5 = x;
}
# cbr - not actual instruction
# clc, clh, cli, cln ... variants on register clearing
# sub bits give which bits in SREG to clear
:clc is phase=1 & ophi16=0x9488 {
$(Cflag) = 0;
}
:clh is phase=1 & ophi16=0x94d8 {
$(Hflag) = 0;
}
:cli is phase=1 & ophi16=0x94f8 {
$(Iflag) = 0;
}
:cln is phase=1 & ophi16=0x94a8 {
$(Nflag) = 0;
}
:cls is phase=1 & ophi16=0x94c8 {
$(Sflag) = 0;
}
:clt is phase=1 & ophi16=0x94e8 {
$(Tflag) = 0;
}
:clv is phase=1 & ophi16=0x94b8 {
$(Vflag) = 0;
}
:clz is phase=1 & ophi16=0x9498 {
$(Zflag) = 0;
}
# clr Rd - really is EOR Rd, Rd
:com RdFull is phase=1 & ophi7=0x4a & RdFull {
RdFull = ~RdFull;
$(Vflag) = 0;
$(Cflag) = 1;
setResultFlags(RdFull);
}
:cp RdFull,RrFull is phase=1 & ophi6=0x05 & RdFull & RrFull {
local x = RdFull - RrFull;
$(Cflag) = (RdFull < RrFull);
$(Vflag) = sborrow(RdFull,RrFull);
setResultFlags(x);
# but doesn't set result into a register
}
:cpc RdFull,RrFull is phase=1 & ophi6=0x1 & RdFull & RrFull {
local res = 0;
doSubtractWithCarry(RdFull,RrFull,res);
res = res; # avoid warning
}
:cpi RdHi,K8 is phase=1 & ophi4=0x3 & RdHi & K8 {
local res = 0;
doSubtract(RdHi,K8,res);
res = res; # avoid warning
}
@ifdef FUSION
# cpi; ldi; cpc sequence
:cpiw f3op1RdPairHi,f3cmpK16" ;ldi "f3op2RdHi,f3cmpK8 is phase=1 & f3op1hi4=0x3 & f3op2hi4=0xe & f3op3hi6=0x1 & f3cmpPairPred & f3cmpLdiPred & f3op1RdPairHi & f3op2RdHi & f3cmpK16 & f3cmpK8 {
local res = 3;
doSubtract(f3op1RdPairHi,f3cmpK16,res);
f3op2RdHi = f3cmpK8;
}
# cp; cpc sequence
:cpw op1RdPair,op1RrPair phase=1 & is op1hi6=0x5 & op2hi6=0x1 & fusion16rrrrPred & op1RdPair & op1RrPair {
local res = op1RdPair - op1RrPair;
$(Vflag) = sborrow(op1RdPair,op1RrPair);
$(Cflag) = (op1RdPair < op1RrPair);
setResultFlags(res);
$(Sflag) = op1RdPair s< op1RrPair;
}
@endif
:cpse RdFull,RrFull is phase=1 & ophi6=0x4 & RdFull & RrFull [ useSkipCond=1; globalset(inst_next,useSkipCond); ] {
SKIP = (RdFull == RrFull);
}
:dec RdFull is phase=1 & ophi7=0x4a & oplow4=0xa & RdFull {
# doesn't set the C flag
$(Vflag) = (RdFull == 0x80);
RdFull = RdFull - 1;
setResultFlags(RdFull);
}
define pcodeop encrypt;
define pcodeop decrypt;
:des op4to7 is phase=1 & ophi8=0x94 & oplow4=0xb & op4to7 {
val:1 = op4to7;
if (Hflg) goto <enc>;
decrypt(val);
goto inst_next;
<enc>
encrypt(val);
}
@if HASEIND == "1"
:eicall is phase=1 & ophi16=0x9519 {
ptr:$(PCBYTESIZE) = inst_next >> 1;
pushPC(ptr);
PC = zext(Z) | (zext(EIND) << 16);
call [PC];
}
:eijmp is phase=1 & ophi16=0x9419 {
PC = zext(Z) | (zext(EIND) << 16);
goto [PC];
}
@endif
@if PCBYTESIZE == "3"
:elpm is phase=1 & ophi16=0x95d8 {
ptr:3 = zext(Z) | (zext(RAMPZ) << 16);
tmp:2 = *[code]:2 (ptr >> 1);
val:2 = (tmp >> (8 * (Z & 0x1)));
R0 = val:1;
}
:elpm RdFull, Z is phase=1 & ophi7=0x48 & oplow4=0x6 & RdFull & Z {
ptr:3 = zext(Z) | (zext(RAMPZ) << 16);
tmp:2 = *[code]:2 (ptr >> 1);
val:2 = (tmp >> (8 * (Z & 0x1)));
RdFull = val:1;
}
ElpmPlus: Z^"+" is Z {}
:elpm RdFull, ElpmPlus is phase=1 & ophi7=0x48 & oplow4=0x7 & RdFull & ElpmPlus {
ptr:3 = zext(Z) | (zext(RAMPZ) << 16);
tmp:2 = *[code]:2 (ptr >> 1);
val:2 = (tmp >> (8 * (Z & 0x1)));
RdFull = val:1;
ptr = ptr + 1;
Z = ptr:2;
RAMPZ = ptr[16,8];
}
@endif
:eor RdFull,RrFull is phase=1 & ophi6=0x9 & RdFull & RrFull {
RdFull = RdFull ^ RrFull;
$(Vflag) = 0;
setResultFlags(RdFull);
}
# Manual uses fmul. I prefer fracmul to distinguish from floating point
:fracmul RdHi,RrHi is phase=1 & ophi9=0x6 & opbit3=1 & RdHi & RrHi { todo(); }
:fracmuls RdHi,RrHi is phase=1 & ophi9=0x7 & opbit3=0 & RdHi & RrHi { todo(); }
:fracmulsu RdHi,RrHi is phase=1 & ophi9=0x7 & opbit3=1 & RdHi & RrHi { todo(); }
:icall is phase=1 & ophi16=0x9509 {
ptr:$(PCBYTESIZE) = inst_next >> 1;
pushPC(ptr);
PC = zext(Z);
call [PC];
}
:ijmp is phase=1 & ophi16=0x9409 {
PC = zext(Z);
goto [PC];
}
# in Rd,A
:in RdFull,Aio6 is phase=1 & ophi5=0x16 & RdFull & Aio6 {
RdFull = Aio6;
}
:in RdFull,SPL is phase=1 & ophi5=0x16 & RdFull & op9to10=3 & oplow4=0xd & SPL {
RdFull = SPL;
}
:in RdFull,SPH is phase=1 & ophi5=0x16 & RdFull & op9to10=3 & oplow4=0xe & SPH {
RdFull = SPH;
}
:in RdFull,SREG is phase=1 & ophi5=0x16 & RdFull & op9to10=3 & oplow4=0xf & SREG {
loadSREG(RdFull);
}
:inc RdFull is phase=1 & ophi7=0x4a & oplow4=0x3 & RdFull {
# inc doesn't set the C flag.
$(Vflag) = RdFull == 0x7f;
RdFull = RdFull + 1;
setResultFlags(RdFull);
}
:jmp abs22dst is phase=1 & (ophi7=0x4a & op1to3=0x6) ... & abs22dst {
PC = &abs22dst;
goto abs22dst;
}
:lac Z,RdFull is phase=1 & ophi7=0x49 & oplow4=0x6 & Z & RdFull {
tmp:1 = *[mem]:1 Z;
tmp = tmp & (0xff - RdFull);
*[mem]:1 Z = tmp;
RdFull = tmp;
}
:las Z,RdFull is phase=1 & ophi7=0x49 & oplow4=0x5 & Z & RdFull {
tmp:1 = *[mem]:1 Z;
tmp = tmp | RdFull;
*[mem]:1 Z = tmp;
RdFull = tmp;
}
:lat Z,RdFull is phase=1 & ophi7=0x49 & oplow4=0x7 & Z & RdFull {
tmp:1 = *[mem]:1 Z;
tmp = tmp ^ RdFull;
*[mem]:1 Z = tmp;
RdFull = tmp;
}
# three forms, really just specifying the increment mode
# ld Rd,X
:ld RdFull,X is phase=1 & ophi7=0x48 & oplow4=0xc & X & RdFull {
tmp:2 = X;
RdFull = *[mem]:1 tmp;
}
# ld Rd,Y; ld Rd,Z
:ld RdFull,RstPtr is phase=1 & ophi7=0x40 & oplow3=0x0 & RdFull & RstPtr {
tmp:2 = RstPtr;
RdFull = *[mem]:1 tmp;
}
# ld Rd,Y+ ; ld Rd, X+; ld Rd, Z+
LdPlus: RstPtr^"+" is RstPtr { tmp:2 = RstPtr; RstPtr = RstPtr + 0x01; export tmp; }
:ld RdFull,LdPlus is phase=1 & ophi7=0x48 & oplow2=0x01 & RdFull & LdPlus {
RdFull = *[mem]:1 LdPlus;
}
# ld Rd,-Y ; ld Rd, -X; ld Rd, -Z
LdPredec: "-"^RstPtr is RstPtr { RstPtr = RstPtr - 0x01; export RstPtr; }
:ld RdFull,LdPredec is phase=1 & ophi7=0x48 & oplow2=0x02 & RdFull & LdPredec {
tmp:2 = LdPredec;
RdFull = *[mem]:1 tmp;
}
# ldd Rd,Y+q
# ldd Rd,Z+q
LddYq: Rstq^"+"^q6 is Rstq & q6 { local ptr = Rstq + zext(q6); export ptr; }
:ldd RdFull,LddYq is phase=1 & ophi2=0x2 & opbit12=0 & opbit9=0 & LddYq & RdFull {
RdFull = *[mem]:1 LddYq;
}
# Rd,K
:ldi RdHi,K8 is phase=1 & ophi4=0xe & RdHi & K8 {
RdHi = K8;
}
@if PCBYTESIZE == "2"
# lds Rd,k
:lds RdFull,next16memPtrVal1 is phase=1 & ophi7=0x48 & oplow4=0 & RdFull; next16memPtrVal1 {
RdFull = next16memPtrVal1;
}
@else
:lds RdFull,next24memPtrVal1 is phase=1 & ophi7=0x48 & oplow4=0 & RdFull; next24memPtrVal1 {
RdFull = next24memPtrVal1;
}
@endif
@ifdef FUSION
# Fuse together consecuitive lds ; lds
#
:ldsw ldswop1RdPair,ldswMemPtrVal2 is phase=1 & ldswop1hi7=0x48 & ldswop2hi7=0x48 & ldswop1low4=0 & ldswop2low4=0 & ldswMemPtrVal2 & ldswop1RdPair & ldswPairPred & ldswConstPairPred {
ldswop1RdPair = ldswMemPtrVal2;
}
@endif
# lds Rd,k Seem to get some problems here... but 16-bit instruction isn't available on all atmega64.
# Furthermore, it will sometimes conflict with ldd Z+q for q=0x2_
#:lds RdHi,A7Ioaddr is ophi5=0x14 & RdHi & A7Ioaddr {
# todo(); # Not currently implemented
# RdHi = A7Ioaddr;
#}
# TODO: lpm semantic behavior needs verification !
# lpm R0
:lpm R0 is phase=1 & ophi16=0x95c8 & R0 {
ptr:$(PCBYTESIZE) = zext(Z);
tmp:$(PCBYTESIZE) = *[code]:$(PCBYTESIZE) (ptr >> 1);
val:$(PCBYTESIZE) = (tmp >> (8 * (Z & 0x1)));
R0 = val:1;
}
# lpm Rd,Z
:lpm RdFull,Z is phase=1 & ophi7=0x48 & op1to3=0x2 & RdFull & Z & opbit0=0 {
ptr:$(PCBYTESIZE) = zext(Z);
tmp:$(PCBYTESIZE) = *[code]:$(PCBYTESIZE) (ptr >> 1);
val:$(PCBYTESIZE) = (tmp >> (8 * (Z & 0x1)));
RdFull = val:1;
}
# lpm Rd,Z+
LpmPlus: Z^"+" is Z {}
:lpm RdFull,LpmPlus is phase=1 & ophi7=0x48 & op1to3=0x2 & RdFull & LpmPlus & opbit0=1 {
ptr:$(PCBYTESIZE) = zext(Z);
tmp:$(PCBYTESIZE) = *[code]:$(PCBYTESIZE) (ptr >> 1);
val:$(PCBYTESIZE) = (tmp >> (8 * (Z & 0x1)));
RdFull = val:1;
Z = Z + 1;
}
# lsl - just an assembly mnemonic for add
:lsr RdFull is phase=1 & ophi7=0x4a & oplow4=0x6 & RdFull {
$(Cflag) = RdFull & 0x01;
RdFull = (RdFull >> 1);
$(Vflag) = $(Cflag);
setResultFlags(RdFull);
}
# mov Rd,Rr
:mov RdFull,RrFull is phase=1 & ophi6=0xb & RdFull & RrFull {
RdFull = RrFull;
}
# movw Rd+1:Rd,Rr+1Rr
:movw Rdw4,Rrw4 is phase=1 & ophi8=0x1 & Rdw4 & Rrw4 {
Rdw4 = Rrw4;
}
:mul RdFull,RrFull is phase=1 & ophi6=0x27 & RdFull & RrFull {
a:2 = zext(RdFull);
b:2 = zext(RrFull);
R1R0 = a * b;
setMulFlags(R1R0);
}
:muls RdHi,RrHi is phase=1 & ophi8=0x2 & RdHi & RrHi {
a:2 = sext(RdHi);
b:2 = sext(RrHi);
R1R0 = a * b;
setMulFlags(R1R0);
}
:mulsu RdHi3,RrHi3 is phase=1 & ophi8=0x3 & opbit7=0 & opbit3=0 & RdHi3 & RrHi3 {
a:2 = sext(RdHi3);
b:2 = zext(RrHi3);
R1R0 = a * b;
setMulFlags(R1R0);
}
:neg RdFull is phase=1 & ophi7=0x4a & oplow4=1 & RdFull {
RdFull = -RdFull;
$(Vflag) = (RdFull == 0x80);
$(Cflag) = (RdFull != 0);
setResultFlags(RdFull);
}
:nop is phase=1 & ophi16=0x0 {
}
:or RdFull,RrFull is phase=1 & ophi6=0xa & RdFull & RrFull {
RdFull = RdFull | RrFull;
$(Vflag) = 0;
setResultFlags(RdFull);
}
:ori RdHi,K8 is phase=1 & ophi4=0x6 & RdHi & K8 {
RdHi = RdHi | K8;
$(Vflag) = 0;
setResultFlags(RdHi);
}
# out A,Rr # Note: Rr occupies the normal Rd position
:out Aio6,RdFull is phase=1 & ophi5=0x17 & Aio6 & RdFull {
Aio6 = RdFull;
}
:out SPL,RdFull is phase=1 & ophi5=0x17 & RdFull & op9to10=3 & oplow4=0xd & SPL {
SPL = RdFull;
}
:out SPH,RdFull is phase=1 & ophi5=0x17 & RdFull & op9to10=3 & oplow4=0xe & SPH {
SPH = RdFull;
}
:out SREG,RdFull is phase=1 & ophi5=0x17 & RdFull & op9to10=3 & oplow4=0xf & SREG {
storeSREG(RdFull);
}
:pop RdFull is phase=1 & ophi7=0x48 & oplow4=0xf & RdFull {
pop8(RdFull);
}
# push Rf # Note: Rr occupies the normal Rd position
:push RdFull is phase=1 & ophi7=0x49 & oplow4=0xf & RdFull {
push8(RdFull);
}
# rcall . is used by the compiler to create space on the stack
:rcall "." is phase=1 & ophi4=0xd & oplow12=0 {
ptr:$(PCBYTESIZE) = inst_next >> 1;
pushPC(ptr);
}
:rcall rel12dst is phase=1 & ophi4=0xd & rel12dst {
ptr:$(PCBYTESIZE) = inst_next >> 1;
pushPC(ptr);
PC = &rel12dst;
call rel12dst;
}
:ret is phase=1 & ophi16=0x9508 {
# Could also handle word size options here
popPC(PC);
return [PC];
}
:reti is phase=1 & ophi16=0x9518 {
$(Iflag) = 1;
popPC(PC);
return [PC];
}
# rjmp k
:rjmp rel12dst is phase=1 & ophi4=0xc & rel12dst {
goto rel12dst;
}
# ROL is ADC Rd,Rd
:ror RdFull is phase=1 & ophi7=0x4a & oplow4=0x7 & RdFull {
local c = $(Cflag);
local cnew = RdFull & 0x01;
RdFull = (c << 7) | (RdFull >> 1);
$(Cflag) = cnew;
$(Nflag) = (RdFull & 0x80) == 0x80;
$(Vflag) = $(Cflag) ^ $(Nflag);
setResultFlags(RdFull);
}
:sbc RdFull,RrFull is phase=1 & ophi6=0x2 & RdFull & RrFull {
doSubtractWithCarry(RdFull,RrFull,RdFull);
}
:sbci RdHi,K8 is phase=1 & ophi4=4 & RdHi & K8 {
doSubtractWithCarry(RdHi,K8,RdHi);
}
@ifdef FUSION
# subi sbci
:subiw op1RdPairHi,K16fuse is phase=1 & op1hi4=0x5 & op2hi4=0x4 & K16fuse & fusion16rkrkPred & op1RdPairHi {
# doSubtract(op1RdPairHi,K16fuse,op1RdPairHi);
local res = op1RdPairHi - K16fuse;
local pre = op1RdPairHi;
$(Vflag) = sborrow(pre,K16fuse);
$(Cflag) = (op1RdPairHi < K16fuse);
op1RdPairHi = res;
setResultFlags(res);
$(Sflag) = pre s< K16fuse;
}
@endif
:sbi Aio5,oplow3 is phase=1 & ophi8=0x9a & Aio5 & oplow3 {
Aio5 = Aio5 | (1 << oplow3);
}
:sbic Aio5,oplow3 is phase=1 & ophi8=0x99 & Aio5 & oplow3 [ useSkipCond=1; globalset(inst_next,useSkipCond); ] {
SKIP = ((Aio5 & (1 << oplow3)) == 0);
}
:sbis Aio5,oplow3 is phase=1 & ophi8=0x9b & Aio5 & oplow3 [ useSkipCond=1; globalset(inst_next,useSkipCond); ] {
SKIP = ((Aio5 & (1 << oplow3)) != 0);
}
:sbiw Rdw2,K6 is phase=1 & ophi8=0x97 & Rdw2 & K6 {
local pre = Rdw2;
Rdw2 = Rdw2 - zext(K6);
$(Cflag) = (pre < zext(K6));
$(Vflag) = sborrow(pre,zext(K6));
setResultFlags(Rdw2);
}
# sbr is an alias for ori
:sbrc RdFull,oplow3 is phase=1 & ophi7=0x7e & opbit3=0 & RdFull & oplow3 [ useSkipCond=1; globalset(inst_next,useSkipCond); ] {
SKIP = ((RdFull & (1 << oplow3)) == 0);
}
:sbrs RdFull,oplow3 is phase=1 & ophi7=0x7f & opbit3=0 & RdFull & oplow3 [ useSkipCond=1; globalset(inst_next,useSkipCond); ] {
SKIP = ((RdFull & (1 << oplow3)) != 0);
}
# More flag setting sec, seh, sei, sen, ses, set, sev, sez
# Implemented as bset
:ser RdHi is phase=1 & ophi8=0xef & oplow4=0xf & RdHi {
RdHi = 0xff;
}
define pcodeop sleep;
:sleep is phase=1 & ophi16=0x9588 {
sleep();
}
define pcodeop store_program_mem; # make this stand out.
:spm Z is phase=1 & ophi16=0x95e8 & Z {
ptr:$(PCBYTESIZE) = zext(Z) << 1;
*[code]:$(PCBYTESIZE) ptr = R1R0;
store_program_mem();
}
SpmPlus: Z^"+" is Z {}
:spm SpmPlus is phase=1 & ophi16=0x95f8 & SpmPlus {
ptr:$(PCBYTESIZE) = zext(Z) << 1;
*[code]:$(PCBYTESIZE) ptr = R1R0;
Z = Z + 1;
store_program_mem();
}
# For stores, see the ld code (just flip bit 9)
:st X, RdFull is phase=1 & ophi7=0x49 & oplow4=0xc & X & RdFull {
tmp:2 = X;
*[mem]:1 tmp = RdFull;
}
# st Rd,Y; st Rd,Z
:st RstPtr, RdFull is phase=1 & ophi7=0x41 & oplow3=0x0 & RdFull & RstPtr {
tmp:2 = RstPtr;
*[mem]:1 tmp = RdFull;
}
# st Rd,Y+ ; st Rd, X+; st Rd, Z+
StPlus: RstPtr^"+" is RstPtr { tmp:2 = RstPtr; RstPtr = RstPtr + 0x01; export tmp; }
:st StPlus, RdFull is phase=1 & ophi7=0x49 & oplow2=0x01 & RdFull & StPlus {
*[mem]:1 StPlus = RdFull;
}
# st Rd,-Y ; st Rd, -X; st Rd, -Z
StPredec: "-"^RstPtr is RstPtr { RstPtr = RstPtr - 0x01; export RstPtr; }
:st StPredec, RdFull is phase=1 & ophi7=0x49 & oplow2=0x02 & RdFull & StPredec {
tmp:2 = StPredec;
*[mem]:1 tmp = RdFull;
}
# std Rd,Y+q; std Rd,Z+q
StdYq: Rstq^"+"^q6 is Rstq & q6 { local ptr = Rstq + zext(q6); export ptr; }
:std StdYq, RdFull is phase=1 & ophi2=0x2 & opbit12=0 & opbit9=1 & RdFull & StdYq {
*[mem]:1 StdYq = RdFull;
}
:sts next16memPtrVal1,RdFull is phase=1 & ophi7=0x49 & oplow4=0 & RdFull; next16memPtrVal1 {
next16memPtrVal1 = RdFull;
}
@ifdef FUSION
# sts ; sts emits backwards with respect to lds; lds
:stsw stswMemPtrVal2,stswop2RdPair is phase=1 & ldswop1hi7=0x49 & ldswop2hi7=0x49 & ldswop1low4=0 & ldswop2low4=0 & stswMemPtrVal2 & stswop2RdPair & stswPairPred & stswConstPairPred {
stswMemPtrVal2 = stswop2RdPair;
}
@endif
# see manual for computation of address for 16-bit STS
:sts RdHi is phase=1 & ophi5=0x15 & RdHi { todo(); }
:sub RdFull,RrFull is phase=1 & ophi6=0x6 & RdFull & RrFull {
doSubtract(RdFull,RrFull,RdFull);
}
# Rd,K
:subi RdHi,K8 is phase=1 & ophi4=5 & RdHi & K8 {
doSubtract(RdHi,K8,RdHi);
}
:swap RdFull is phase=1 & ophi7=0x4a & oplow4=2 & RdFull {
RdFull = (RdFull >> 4) | (RdFull << 4);
}
# tst is AND Rd,Rd
define pcodeop watchdog_reset;
:wdr is phase=1 & ophi16=0x95a8 {
watchdog_reset();
}
:xch RdFull is phase=1 & ophi7=0x49 & oplow4=0x4 & RdFull {
ptr:2 = Z;
local tmp = *[mem]:1 ptr;
*[mem]:1 ptr = RdFull;
RdFull = tmp;
}