Skip to content

Instantly share code, notes, and snippets.

@murachue
Created May 18, 2019 08:32
Show Gist options
  • Save murachue/2e63f7f2d03d79ba92b5e67b1e1499a4 to your computer and use it in GitHub Desktop.
Save murachue/2e63f7f2d03d79ba92b5e67b1e1499a4 to your computer and use it in GitHub Desktop.
INCOMPLETE SH-2 Ghidra processor module
<?xml version="1.0" encoding="UTF-8"?>
<compiler_spec>
<global>
<range space="RAM"/>
</global>
<stackpointer register="r15" space="RAM" growth="negative"/>
<returnaddress>
<register name="pr"/>
</returnaddress>
<default_proto>
<prototype name="__stdcall" extrapop="0" stackshift="0" strategy="register">
<input>
<pentry minsize="1" maxsize="4">
<register name="r4"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="r5"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="r6"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="r7"/>
</pentry>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="0" space="stack"/>
</pentry>
</input>
<output>
<pentry minsize="1" maxsize="4">
<register name="r0"/>
</pentry>
</output>
<unaffected>
<register name="r8"/>
<register name="r9"/>
<register name="r10"/>
<register name="r11"/>
<register name="r12"/>
<register name="r13"/>
<register name="r14"/>
<register name="r15"/> <!-- sp -->
</unaffected>
</prototype>
</default_proto>
</compiler_spec>
<?xml version="1.0" encoding="UTF-8"?>
<language_definitions>
<language processor="SH-2"
endian="big"
size="32"
variant="default"
version="1.0"
slafile="sh2.sla"
processorspec="sh2.pspec"
id="SH2:BE:32:default">
<description>SH-2 Family</description>
<compiler name="default" spec="sh2.cspec" id="default"/>
<external_name tool="IDA-PRO" name="sh2"/>
</language>
</language_definitions>
<?xml version="1.0" encoding="UTF-8"?>
<processor_spec>
<programcounter register="pc"/>
</processor_spec>
# INCOMPLETE sleigh specification file for Hitachi SH-2
# just for learning SLEIGH
# you should use https://github.com/VGKintsugi/Ghidra-SegaSaturn-Processor
define endian=big;
define alignment=2;
define space RAM type=ram_space size=4 default;
define space register type=register_space size=4;
define register offset=0x00 size=4 [ r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 ];
define register offset=0x40 size=4 [ sr GBR VBR TBR ]; # ctlreg
define register offset=0x80 size=4 [ mach macl pr pc ]; # sysreg
#define bitrange
# BO=sr[14,1]
# CS=sr[13,1]
# M=sr[9,1]
# Q=sr[8,1]
# I=sr[4,4]
# S=sr[1,1]
# T=sr[0,1];
# "define bitrange" produces wrong sla output... use macro.
@define SR_BO "sr[14,1]"
@define SR_CS "sr[13,1]"
@define SR_M "sr[9,1]"
@define SR_Q "sr[8,1]"
@define SR_I "sr[4,1]"
@define SR_S "sr[1,1]"
@define SR_T "sr[0,1]"
# TODO: ... or emulate sr on ldc/sdc and define those bits as individual registers.
# TODO: floating points
define token insn (16)
op16 = (0,15)
op = (12,15)
n = (8,11)
op8 = (8,15)
m = (4,7)
mc = (4,7)
ms = (4,7)
d4u = (0,3)
d8 = (0,7) signed # for branch
d8u = (0,7)
d12 = (0,11) signed
i = (0,7) signed
iu = (0,7)
opl = (0,3)
opl8 = (0,7)
;
attach variables [n m] [r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15];
attach variables [mc] [sr GBR VBR _ _ _ _ _ _ _ _ _ _ _ _ _];
attach variables [ms] [mach macl pr _ _ _ _ _ _ _ _ _ _ _ _ _];
Rel12: addr is d12 [ addr = inst_start + 4 + d12 * 2; ] { export *:4 addr; }
# FIXME: 20190408 I don't know why sleigh-compiler crash when write in a line but not if Rel8+b*
Rel8: addr is d8 [ addr = inst_start + 4 + d8 * 2; ] { export *:4 addr; }
:stc mc,n is op=0 & n & mc & opl=2 { n = mc; }
:bsrf n is op=0 & n & opl8=0x03 { pr = inst_start + 4; npc = inst_start + 4 + n; delayslot(1); call [npc]; } # reusing n as m
:braf n is op=0 & n & opl8=0x23 { npc = inst_start + 4 + n; delayslot(1); goto [npc]; } # XXX: reusing n as m
:mov.b m,@(r0,n) is op=0 & n & m & opl=4 & r0 { *:1 (r0 + n) = m:1; }
:mov.w m,@(r0,n) is op=0 & n & m & opl=5 & r0 { *:2 (r0 + n) = m:2; }
:mov.l m,@(r0,n) is op=0 & n & m & opl=6 & r0 { *:4 (r0 + n) = m; }
:mul.l m,n is op=0 & n & m & opl=7 { macl = n * m; } # no mach change
:nop is op16=0x0009 { }
:div0u is op16=0x0019 { $(SR_M) = 0; $(SR_Q) = 0; $(SR_T) = 0; }
:movt n is op=0 & n & opl8=0x29 { n = zext($(SR_T)); }
:sts ms,n is op=0 & n & ms & opl=10 { n = ms; }
:rts is op16=0x000B { npc = pr; delayslot(1); return [npc]; }
:rte is op16=0x002B { npc = *:4 r15; r15 = r15 + 4; sr = *:4 r15 & 0x000063F3; r15 = r15 + 4; delayslot(1); goto [npc]; }
:mov.b @(r0,m),n is op=0 & n & m & opl=12 & r0 { n = sext(*:1 (r0 + m)); }
:mov.w @(r0,m),n is op=0 & n & m & opl=13 & r0 { n = sext(*:2 (r0 + m)); }
:mov.l @(r0,m),n is op=0 & n & m & opl=14 & r0 { n = *:4 (r0 + m); }
:mov.l m,@(d4ul2,n) is op=1 & n & m & d4u [ d4ul2 = d4u * 4; ] { *:4 (n + d4ul2) = m:4; }
:mov.b m,@n is op=2 & m & n & opl=0 { *:1 n = m:1; }
:mov.w m,@n is op=2 & m & n & opl=1 { *:2 n = m:2; }
:mov.l m,@n is op=2 & m & n & opl=2 { *:4 n = m; }
:mov.b m,@-n is op=2 & m & n & opl=4 { n = n - 4; *:1 n = m:1; }
:mov.w m,@-n is op=2 & m & n & opl=5 { n = n - 4; *:2 n = m:2; }
:mov.l m,@-n is op=2 & m & n & opl=6 { n = n - 4; *:4 n = m; }
:div0s m,n is op=2 & n & m & opl=7 { $(SR_Q) = ((n & 0x80000000) != 0); $(SR_M) = ((m & 0x80000000) != 0); $(SR_T) = $(SR_M) ^ $(SR_Q); }
:tst m,n is op=2 & n & m & opl=8 { $(SR_T) = ((n & m) == 0); }
:and m,n is op=2 & n & m & opl=9 { n = n & m; }
:xor m,n is op=2 & n & m & opl=10 { n = n ^ m; }
:or m,n is op=2 & n & m & opl=11 { n = n | m; }
:cmp/"str" m,n is op=2 & n & m & opl=12 {
t = n ^ m;
hh = (((t >> 24) & 255) == 0);
hl = (((t >> 16) & 255) == 0);
lh = (((t >> 8) & 255) == 0);
ll = ((t & 255) == 0);
$(SR_T) = (hh || hl || lh || ll); # T=1 if some byte(s) are matched
}
:xtrct m,n is op=2 & n & m & opl=13 { n = ((m & 0xFFFF) << 16) | (n >> 16); }
:mulu.w m,n is op=2 & n & m & opl=14 { macl = zext(n:2) * zext(m:2); }
:muls.w m,n is op=2 & n & m & opl=15 { macl = sext(n:2) * sext(m:2); }
:cmp/"eq" m,n is op=3 & n & m & opl=0 { $(SR_T) = (n == m); }
:cmp/"hs" m,n is op=3 & n & m & opl=2 { $(SR_T) = (n >= m); } # high-or-same?
:cmp/"ge" m,n is op=3 & n & m & opl=3 { $(SR_T) = (n s>= m); }
:div1 m,n is op=3 & n & m & opl=4 {
oldq = $(SR_Q);
$(SR_Q) = ((n & 0x80000000) != 0);
n = (n << 1) | zext($(SR_T));
negater:4 = 1 - (sext(oldq != $(SR_M)) * 2); # sext or zext.
oldn = n;
# if oldq==M then {n-=m;c=n>oldn} else {n+=m;c=n<oldn}
n = n - (m * negater);
c = (n * negater) > (oldn * negater);
# if M==Q then Q=c; else Q=(c==0);
$(SR_Q) = c ^ ($(SR_M) != $(SR_Q));
$(SR_T) = ($(SR_Q) == $(SR_M));
}
:dmulu.l m,n is op=3 & n & m & opl=5 { t:8 = zext(m) * zext(n); mach = t(4); macl = t:4; }
:cmp/"hi" m,n is op=3 & n & m & opl=6 { $(SR_T) = (n > m); }
:cmp/"gt" m,n is op=3 & n & m & opl=7 { $(SR_T) = (n s> m); }
:sub m,n is op=3 & n & m & opl=8 { n = n - m; }
:subc m,n is op=3 & n & m & opl=10 { on = n; r = n - m; n = r - zext($(SR_T)); $(SR_T) = (on < r) | (r < n); }
:add m,n is op=3 & n & m & opl=12 { n = n + m; }
:dmuls.l m,n is op=3 & n & m & opl=13 { t:8 = sext(m) * sext(n); mach = t(4); macl = t:4; }
# users manual style # or using carry() style (seems produce same decompile)
:addc m,n is op=3 & n & m & opl=14 { tmp1 = n + m; tmp0 = n; n = tmp1 + zext($(SR_T)); $(SR_T) = ((tmp0 > tmp1) | (tmp1 > n)); } # { c1 = carry(n, m); n = n + m; c2 = carry(n, zext($(SR_T))); n = n + zext($(SR_T)); $(SR_T) = (c1 | c2); }
:shll n is op=4 & n & opl8=0x00 { $(SR_T) = ((n & 0x80000000) != 0); n = n << 1; }
:dt n is op=4 & n & opl8=0x10 { n = n - 1; $(SR_T) = (n == 0); }
:shal n is op=4 & n & opl8=0x20 { $(SR_T) = ((n & 0x80000000) != 0); n = n << 1; } # same as shll.
:shlr n is op=4 & n & opl8=0x01 { $(SR_T) = ((n & 1) != 0); n = n >> 1; }
:cmp/"pz" n is op=4 & n & opl8=0x11 { $(SR_T) = (n s>= 0); }
:shar n is op=4 & n & opl8=0x21 { $(SR_T) = ((n & 1) != 0); n = n s>> 1; }
:sts.l ms,@-n is op=4 & n & ms & opl=2 { n = n - 4; *n = ms; }
:stc.l mc,@-n is op=4 & n & mc & opl=3 { n = n - 4; *n = mc; }
:rotl n is op=4 & n & opl8=0x04 { $(SR_T) = ((n & 0x80000000) != 0); n = (n << 1) | (n >> 31); }
:rotcl n is op=4 & n & opl8=0x24 { ot = $(SR_T); $(SR_T) = ((n & 0x80000000) != 0); n = (n << 1) | zext(ot); }
:rotr n is op=4 & n & opl8=0x05 { $(SR_T) = ((n & 1) != 0); n = (n >> 1) | (n << 31); }
:cmp/"pl" n is op=4 & n & opl8=0x15 { $(SR_T) = (n s> 0); }
:rotcr n is op=4 & n & opl8=0x25 { ot = $(SR_T); $(SR_T) = ((n & 1) != 0); n = (zext(ot) << 31) | (n >> 1); }
:lds.l @n+,ms is op=4 & n & ms & opl=6 { ms = *n; n = n + 4; }
:ldc.l @n+,mc is op=4 & n & mc & opl=7 { mc = *n; n = n + 4; }
:shll2 n is op=4 & n & opl8=0x08 { n = n << 2; }
:shll8 n is op=4 & n & opl8=0x18 { n = n << 8; }
:shll16 n is op=4 & n & opl8=0x28 { n = n << 16; }
:shlr2 n is op=4 & n & opl8=0x09 { n = n >> 2; }
:shlr8 n is op=4 & n & opl8=0x19 { n = n >> 8; }
:shlr16 n is op=4 & n & opl8=0x29 { n = n >> 16; }
:lds n,ms is op=4 & ms & n & opl=10 { ms = n; }
:jsr @n is op=4 & n & opl8=0x0B { pr = inst_start + 4; npc = n; delayslot(1); call [npc]; } # XXX: reusing n as m
:jmp @n is op=4 & n & opl8=0x2B { npc = n; delayslot(1); goto [npc]; }
# XXX: "m" and "n(c)" naming is reversed to reuse fields...
:ldc n,mc is op=4 & n & mc & opl=14 { mc = n; }
:mov.l @(d4ul2,m),n is op=5 & n & m & d4u [ d4ul2 = d4u * 4; ] { n = *:4 (m + d4ul2); }
:mov.b @m,n is op=6 & n & m & opl=0 { n = sext(*:1 m); }
:mov.w @m,n is op=6 & n & m & opl=1 { n = sext(*:2 m); }
:mov.l @m,n is op=6 & n & m & opl=2 { n = *:4 m; }
:mov m,n is op=6 & m & n & opl=3 { n = m; }
:mov.b @m+,n is op=6 & m & n & opl=4 { n = sext(*:1 m); m = m + 4; }
:mov.w @m+,n is op=6 & m & n & opl=5 { n = sext(*:2 m); m = m + 4; }
:mov.l @m+,n is op=6 & m & n & opl=6 { n = *:4 m; m = m + 4; }
:not m,n is op=6 & n & m & opl=7 { n = ~m; }
:neg m,n is op=6 & n & m & opl=11 { n = -m; }
:extu.b m,n is op=6 & n & m & opl=12 { n = zext(m:1); }
:extu.w m,n is op=6 & n & m & opl=13 { n = zext(m:2); }
:exts.b m,n is op=6 & n & m & opl=14 { n = sext(m:1); }
:exts.w m,n is op=6 & n & m & opl=15 { n = sext(m:2); }
:add #i,n is op=7 & n & i { n = n + i; }
:mov.b r0,@(d4u,m) is op8=0x80 & m & d4u & r0 { *:1 (m + d4u ) = r0:1; } # XXX: reusing m as n
:mov.w r0,@(d4ul1,m) is op8=0x81 & m & d4u & r0 [ d4ul1 = d4u * 2; ] { *:2 (m + d4ul1) = r0:2; } # XXX: reusing m as n
:mov.b @(d4u,m),r0 is op8=0x84 & m & d4u & r0 { r0 = sext(*:1 (m + d4u )); }
:mov.w @(d4ul1,m),r0 is op8=0x85 & m & d4u & r0 [ d4ul1 = d4u * 2; ] { r0 = sext(*:2 (m + d4ul1)); }
:cmp/"eq" #i,"r0" is op8=0x88 & i { $(SR_T) = (r0 == sext(i:1)); } # XXX: sleigh-compiler can't know i is 1byte??
#:bt pcrel8 is op8=0x89 & d8 [ pcrel8 = inst_start + 4 + (d8 << 1); ] { if($(SR_T)) goto *:4(inst_start + 4 + sext(d8 << 1)); }
:bt Rel8 is op8=0x89 & Rel8 { if($(SR_T)) goto Rel8; }
#:bf pcrel8 is op8=0x8B & d8 [ pcrel8 = inst_start + 4 + (d8 << 1); ] { if(!$(SR_T)) goto (inst_start + 4 + sext(d8 << 1)); }
:bf Rel8 is op8=0x8B & Rel8 { if(!$(SR_T)) goto Rel8; }
# NOTE: SR_T must be kept while delayslot() because it will rewrite T.
#:bt/"s" pcrel8 is op8=0x8D & d8 [ pcrel8 = inst_start + 4 + (d8 << 1); ] { tt = $(SR_T); delayslot(1); if(tt) goto (inst_start + 4 + sext(d8 << 1)); }
:bt/"s" Rel8 is op8=0x8D & Rel8 { tt = $(SR_T); delayslot(1); if(tt) goto Rel8; }
#:bf/"s" pcrel8 is op8=0x8F & d8 [ pcrel8 = inst_start + 4 + (d8 << 1); ] { tt = $(SR_T); delayslot(1); if(!tt) goto (inst_start + 4 + sext(d8 << 1)); }
:bf/"s" Rel8 is op8=0x8F & Rel8 { tt = $(SR_T); delayslot(1); if(!tt) goto Rel8; }
#:mov.w @(d8ul1,pc),n is op=9 & n & d8u & pc [ d8ul1 = d8u * 2; ] { pval:4 = inst_start + 4 + d8ul1; n = sext(*:2 pval); }
:mov.w @(rel),n is op=9 & n & d8u [ rel = inst_start + 4 + d8u * 2; ] { tmp:4 = rel; n = sext(*:2 tmp); } # XXX: WTF unknown size!? must use tmp to specify size.
#:bra rel is op=10 & d12 [ rel = inst_start + 4 + d12 * 2; ] { delayslot(1); goto [rel:4]; } # this does not resolve operand as label.
:bra Rel12 is op=10 & Rel12 { delayslot(1); goto Rel12; }
:bsr Rel12 is op=11 & Rel12 { pr = inst_start + 4; delayslot(1); call Rel12; }
#:mova @(d8ul2,pc),r0 is op8=0xC7 & d8u & pc & r0 [ d8ul2 = d8u << 2; ] { r0 = ((inst_start + 4) & ~3) + d8ul2; }
:mova rel,r0 is op8=0xC7 & d8u & r0 [ rel = ((inst_start + 4) & ~3) + d8u * 4; ] { r0 = rel; }
:tst #iu,r0 is op8=0xC8 & iu & r0 { $(SR_T) = ((r0 & iu) == 0); }
:and #iu,r0 is op8=0xC9 & iu & r0 { r0 = r0 & iu; }
:xor #iu,r0 is op8=0xCA & iu & r0 { r0 = r0 ^ iu; }
:or #iu,r0 is op8=0xCB & iu & r0 { r0 = r0 | iu; }
# TODO: tst #iu,@(r0,GBR) is op8=0xCC and iu
# TODO: and #iu,@(r0,GBR) is op8=0xCD and iu
# TODO: xor #iu,@(r0,GBR) is op8=0xCE and iu
# TODO: or #iu,@(r0,GBR) is op8=0xCF and iu
#pcrlc: #c is d8u { b:4 = (inst_start + 4) & ~3; p:4 = b + zext(d8u); c = *:4 p; export c; }
#:mov.l pcrlc,n is op=13 & n & pcrlc { n = pcrlc; }
#:mov.l @(d8ul2,pc),n is op=13 & n & d8u & pc [ d8ul2 = d8u << 2; ] { pval:4 = ((inst_start + 4) & ~3) + (d8u << 2); n = *pval; }
#:mov.l @(rel),n is op=13 & n & d8u [ rel = ((inst_start + 4) & ~3) + d8u * 4; ] { n = *:4 rel; }
:mov.l @(rel),n is op=13 & n & d8u [ rel = ((inst_start + 4) & ~3) + d8u * 4; ] { rela:4 = ((inst_start + 4) & ~3) + d8u * 4; n = *:4 rela; } # WTF unknown size!?
:mov #i,n is op=14 & n & i { n = i; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment