aboutsummaryrefslogtreecommitdiff
path: root/risugen
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2011-03-09 18:38:02 +0000
committerPeter Maydell <peter.maydell@linaro.org>2011-03-10 16:07:00 +0000
commit0fd0006075ed71727c625329051218982ec0be78 (patch)
tree34f6aa63c2ff73a27ce7b9eb010f1e3510b9d1ae /risugen
parentbeec3256cf96a24fe4009a448479a49b8669c3d5 (diff)
risugen: handle reg_plus_reg{,_shifted} addressing mode
Add support for reg_plus_reg and reg_plus_reg_shifted addressing modes.
Diffstat (limited to 'risugen')
-rwxr-xr-xrisugen82
1 files changed, 82 insertions, 0 deletions
diff --git a/risugen b/risugen
index 327dd66..0ccd823 100755
--- a/risugen
+++ b/risugen
@@ -146,6 +146,45 @@ sub write_sub_rrr($$$)
}
}
+# valid shift types
+my $SHIFT_LSL = 0;
+my $SHIFT_LSR = 1;
+my $SHIFT_ASR = 2;
+my $SHIFT_ROR = 3;
+
+sub write_sub_rrrs($$$$$)
+{
+ # sub rd, rn, rm, shifted
+ my ($rd, $rn, $rm, $type, $imm) = @_;
+ $type = $SHIFT_LSL if $imm == 0;
+ if ($imm == 32 && ($type == $SHIFT_LSR || $type == $SHIFT_ASR)) {
+ $imm = 0;
+ }
+ die "write_sub_rrrs: bad shift immediate $imm\n" if $imm < 0 || $imm > 31;
+ if ($is_thumb) {
+ # enc T2
+ my ($imm3, $imm2) = ($imm >> 2, $imm & 3);
+ insn16(0xeba0 | $rn);
+ insn16(($imm3 << 12) | ($rd << 8) | ($imm2 << 6) | ($type << 4) | $rm);
+ } else {
+ # enc A1
+ insn32(0xe0400000 | ($rn << 16) | ($rd << 12) | ($imm << 7) | ($type << 5) | $rm);
+ }
+}
+
+sub write_mov_rr($$)
+{
+ my ($rd, $rm) = @_;
+ if ($is_thumb) {
+ # enc T3
+ insn16(0xea4f);
+ insn16(($rd << 8) | $rm);
+ } else {
+ # enc A1
+ insn32(0xe1a00000 | ($rd << 12) | $rm);
+ }
+}
+
sub write_mov_ri($$)
{
my ($rd, $imm) = @_;
@@ -389,6 +428,49 @@ sub reg_plus_imm($$@)
return $base;
}
+sub reg_plus_reg_shifted($$$@)
+{
+ # handle reg + reg LSL imm addressing mode
+ my ($base, $idx, $shift, @trashed) = @_;
+ die "reg_plus_reg_shifted: bad shift size\n" if ($shift < 0 || $shift > 3);
+ my $savedidx = 0;
+ if ($idx == 0) {
+ # save the index into some other register for the
+ # moment, because the risuop will trash r0
+ $idx = 1;
+ $idx++ if $idx == $base;
+ $savedidx = 1;
+ write_mov_rr($idx, 0);
+ }
+
+ # Get a random offset within the memory block, of the
+ # right alignment.
+ my $offset = rand(2048) & ~3;
+ write_mov_ri(0, $offset);
+ write_risuop($OP_GETMEMBLOCK);
+ # Now r0 is the address we want to do the access to,
+ # so set the basereg by doing the inverse of the
+ # addressing mode calculation, ie base = r0 - idx LSL imm
+ # LSL x is shift type 0,
+ write_sub_rrrs($base, 0, $idx, $SHIFT_LSL, $shift);
+ if ($savedidx) {
+ # We can move this back to r0 now
+ write_mov_rr(0, $idx);
+ } elsif ($base != 0) {
+ write_mov_ri(0, 0);
+ }
+ if (grep $_ == $base, @trashed) {
+ return -1;
+ }
+ return $base;
+}
+
+sub reg_plus_reg($$@)
+{
+ my ($base, $idx, @trashed) = @_;
+ return reg_plus_reg_shifted($base, $idx, 0, @trashed);
+}
+
sub eval_with_fields($$$$) {
# Evaluate the given block in an environment with Perl variables
# set corresponding to the variable fields for the insn.