# ========== Copyright Header Begin ==========================================
# 
# OpenSPARC T2 Processor File: arch_diags.pm
# Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved
# 4150 Network Circle, Santa Clara, California 95054, U.S.A.
#
# * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# For the avoidance of doubt, and except that if any non-GPL license 
# choice is available it will apply instead, Sun elects to use only 
# the General Public License version 2 (GPLv2) at this time for any 
# software where a choice of GPL license versions is made 
# available with the language indicating that GPLv2 or any later version 
# may be used, or where a choice of which version of the GPL is applied is 
# otherwise unspecified. 
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 
# CA 95054 USA or visit www.sun.com if you need additional information or 
# have any questions. 
# 
# ========== Copyright Header End ============================================

package arch_diags;

use strict;
use BigIntSupport;
use Getopt::Long;

require Exporter;
use vars qw(@ISA @EXPORT);

my @ISA         = qw(Exporter);
use vars          qw(%opt);
my @EXPORT      = qw(%opt);

my @ld_freg    = ("ld", "ldd", "ldq", "ldx");

my @fregs      = ( "%f0",  "%f1",  "%f2",  "%f3",  "%f4",  "%f5",  "%f6",  "%f7",
                "%f8",  "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15",
               "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23",
               "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31" );

my @fregs_dbl  = ( 0,  2,  4,  6,  8, 10, 12, 14, 16, 18,  
               20, 22, 24, 26, 28, 30, 32, 34, 36, 38,  
               40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 
               60, 62);   


my @iregs      = ( "%r0",  "%r1",  "%r2",  "%r3",  "%r4",  "%r5",  "%r6",  "%r7",
                "%r8",  "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
               "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23",
               "%r24", "%r25", "%r26", "%r27", "%r28", "%r29", "%r30", "%r31" );

my @walking1_iregs      = (0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x1f); 
my @walking1_imm13      = (0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20,0x40,0x80,
                       0x100, 0x200, 0x400, 0x800, 0x1000, 0x1fc0);

my $ILLIMM = 0x80000000;


#my @all_fbfcc = ("fba",  "fbn",   "fbu", "fbg", "fbug", "fbl", "fbul", "fblg", "fbne", "fbe", "fbue",
#                 "fbge", "fbuge", "fble", "fbule", "fbo"); 
my @imm22_targets = (0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x3ff000, 0x3ffff0);

my @imm19_targets = (0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x7f000, 0x7fff0);

my %mem_inst = (ld => 1, ldd => 1,  lda => 1, ldda => 1);

my %cti_inst = (
                   ba     => 1, bn     => 1, bne    => 1, be    => 1, bg     => 1,  ble   => 1,
                   bge    => 1, bl     => 1, bgu    => 1, bleu  => 1, bcc    => 1,  bcs   => 1,
                   bpos   => 1, bneg   => 1, bvc    => 1, bvs   => 1, 
                   fba    => 1, fbn    => 1, fbu    => 1, fbg   => 1, fbug    => 1, fbl   => 1,
                   fbul   => 1, fblg   => 1, fbne   => 1, fbe   => 1, fbue    => 1, fbge  => 1,
                   fbuge  => 1, fble   => 1, fbule  => 1, fbo   => 1
               );

    %opt     = (
               name          => "",    # name of the diag
               thrd_count    => 1,     
               thrd_stride   => 1,
               super         => 0,     # run in supervisor mode
               hyper         => 0,     # run in hypervisor mode
               enboff        => 0,     # run in hypervisor mode
               inst          => "",    # instruction to execute
               inst_group    => "",    # instruction to execute
               illinst       => 0,     # generate illinst trap
               fpdis         => 0,     # generate fpdisable trap
               dmisalgn      => 0,     # generate double-misalign trap
               misalgn       => 0,     # generate misalign trap
               dataacc       => 0,     # generate dataacc trap
               privact       => 0,     # generate privact trap
               vawatch       => 0,     # generate vamatch trap
               seltrap       => 0,     # generate vamatch trap
               alldest       => 0,     # try all dest regs
               range_param   => 0,     # generic range.. of any type
               mem_inst      => 0,
               cti_inst      => 0
              );


my @options =
   qw(
      name=s
      thrd_count=i
      thrd_stride=i
      super=i
      hyper=i
      enboff=i
      inst=s
      inst_group=s
      illinst=i
      fpdis=i
      dmisalgn=i
      misalgn=i
      dataacc=i
      privact=i
      vawatch=i
      seltrap=i
      alldest=i
      range_param=i
      mem_inst=i
      cti_inst=i
     );

sub setup_diag_options() {
    GetOptions(\%opt, @options);

    my @diag_run_args = split(/_/, $opt{name});
    my $index = 0;
    printf("!Diag run options :\n");
    foreach my $diag_run_arg (@diag_run_args) {
        if($index == 0) {
            $opt{inst_group} = $diag_run_arg;
            printf("!inst_group = %-20s\n", $opt{inst_group});
        }
        elsif($index == 1) {
            $opt{inst} = $diag_run_arg;
            printf("!inst = %-20s\n", $opt{inst});
        }
        else {
            $opt{$diag_run_arg}=1; 
            printf("!%s = %d\n", $diag_run_arg, $opt{$diag_run_arg});
        }
        $index++;
    }
    $opt{mem_inst} = 1 if($mem_inst{$opt{inst}});
    $opt{cti_inst} = 1 if($cti_inst{$opt{inst}});
}

my $locked_regs = 0;
sub lock_regs() {
    my (@regs) = @_; 
    foreach my $reg (@regs) {
        if(($locked_regs & (0x1 << $reg)) == 0) {
            $locked_regs |= (0x1 << $reg);
        }
        else {
            printf("ERROR: reg %d is already locked\n", $reg);
        }
    }
    printf("!lock_temp_reg locked regs = 0x%x\n", $locked_regs);
}
sub release_regs() {
    my (@regs) = @_;
    foreach my $reg (@regs) {
        if($locked_regs & (0x1 << $reg)) {
            $locked_regs ^= (0x1 << $reg);
        }
        else {
            printf("ERROR: reg %d was never locked\n", $reg);
        }
    }
    printf("!release_temp_reg locked regs = 0x%x\n", $locked_regs);
}
sub release_all_regs() {
    $locked_regs = 0;
    printf("!release all locked regs = 0x%x\n", $locked_regs);
}

sub pick_tmp_reg() {
    my ($ex1, $ex2) = @_;
    foreach my $reg (1..31) {
        if($reg != $ex1 && $reg != $ex2 && (($locked_regs & (0x1 << $reg)) == 0)) { 
            $locked_regs |= (0x1 << $reg);
            printf("!pick temp locked regs = 0x%x\n", $locked_regs);
            return ($reg); 
        }
    }
    printf("ERROR: If here, means something went wrong in pick_tmp_reg\n");
}

sub possible_tmp_reg() {
    my ($ex1, $ex2) = @_;
    foreach my $reg (1..31) {
        if($reg != $ex1 && $reg != $ex2 && (($locked_regs & (0x1 << $reg)) == 0)) { 
            printf("!possible temp locked regs = 0x%x\n", $locked_regs);
            return ($reg); 
        }
    }
    printf("ERROR: If here, means something went wrong in pick_tmp_reg\n");
}

sub setx() {
    my ($va, $tmp_reg, $dest) = @_;
    printf("setx 0x%s, %%r%d, %%r%d\n", bigint2hex($va), $tmp_reg, $dest);
}

sub setx_label() {
    my ($label, $tmp_reg, $dest) = @_;
    printf("setx %s, %%r%d, %%r%d\n", $label, $tmp_reg, $dest);
}

sub mov() {
    my ($rs1, $imm, $dest) = @_;
    if($imm == $ILLIMM) {
        printf("mov %%r%d, %%r%d\n", $rs1, $dest);
    }
    else {
        printf("mov 0x%x, %%r%d\n",  $imm, $dest);
    }
}

sub or_regs() {
    my ($rs1, $rs2, $imm, $dest) = @_;
    if($imm == $ILLIMM) {
        printf("or %%r%d, %%r%d, %%r%d\n", $rs1, $rs2, $dest);
    }
    else {
        printf("or %%r%d, 0x%x, %%r%d\n",  $rs1, $imm, $dest);
    }
}

sub and_regs() {
    my ($rs1, $rs2, $imm, $dest) = @_;
    if($imm == $ILLIMM) {
        printf("and %%r%d, %%r%d, %%r%d\n", $rs1, $rs2, $dest);
    }
    else {
        printf("and %%r%d, 0x%x, %%r%d\n",  $rs1, $imm, $dest);
    }
}

sub add_regs() {
    my ($rs1, $rs2, $imm, $dest) = @_;
    if($imm == $ILLIMM) {
        printf("add %%r%d, %%r%d, %%r%d\n", $rs1, $rs2, $dest);
    }
    else {
        printf("add %%r%d, 0x%x, %%r%d\n",  $rs1, $imm, $dest);
    }
}

sub load_reg() {
    my ($inst, $rs1, $rs2, $imm, $imm_asi, $dest_type, $dest_num) = @_;
    if($inst eq "ldub" || $inst eq "ld" || $inst eq "ldd") {
        &load($inst, $rs1, $rs2, $imm, $dest_type, $dest_num);
    }
    if($inst eq "lda" || $inst eq "ldda" || $inst eq "ldxa") {
        &load_alt($inst, $rs1, $rs2, $imm, $imm_asi, $dest_type, $dest_num);
    }
}

sub load() {
    my ($inst, $rs1, $rs2, $imm, $dest_type, $dest_num) = @_;
    if($imm == $ILLIMM) {
        printf("%s [%%r%d+%%r%d], %s%d\n", $inst, $rs1, $rs2, $dest_type, $dest_num);
    }
    else {
        printf("%s [%%r%d+0x%x], %s%d\n", $inst, $rs1, $imm, $dest_type, $dest_num);
    }
}

sub store() {
    my ($inst, $src_type, $src_num, $rs1, $rs2, $imm) = @_;
    if($imm == $ILLIMM) {
        printf("%s %s%d, [%%r%d+%%r%d]\n", $inst, $src_type, $src_num, $rs1, $rs2);
    }
    else {
        printf("%s %s%d, [%%r%d+0x%x]\n", $inst, $src_type, $src_num, $rs1, $imm);
    }
}

sub load_alt() {
    my ($inst, $rs1, $rs2, $imm, $imm_asi, $dest_type, $dest_num) = @_;
    if($imm == $ILLIMM) {
        if($imm_asi == $ILLIMM) {
            printf("%s [%%r%d+%%r%d] %%asi, %s%d\n", $inst, $rs1, $rs2, $dest_type, $dest_num);
        }
        else {
            printf("%s [%%r%d+%%r%d] 0x%x, %s%d\n", $inst, $rs1, $rs2, $imm_asi, $dest_type, $dest_num);
        }
    }
    else {
        if($imm_asi == $ILLIMM) {
            printf("%s [%%r%d+0x%x] %%asi, %s%d\n", $inst, $rs1, $imm, $dest_type, $dest_num);
        }
        else {
            printf("ta T_CHANGE_PRIV\n") if($opt{super} == 0);
            printf("wr %%r0, 0x%x, %%asi\n", $imm_asi); 
            printf("ta T_CHANGE_NONPRIV\n") if($opt{super} == 0);
            printf("%s [%%r%d+0x%x] %%asi, %s%d\n", $inst, $rs1, $imm, $dest_type, $dest_num);
        }
    }
}

sub store_alt() {
    my ($inst, $dest_type, $dest_num, $rs1, $rs2, $imm, $imm_asi) = @_;

    if($imm == $ILLIMM) {
        if($imm_asi == $ILLIMM) {
            printf("%s %s%d, [%%r%d+%%r%d] %%asi\n", $inst, $dest_type, $dest_num, $rs1, $rs2);
        }
        else {
            printf("%s %s%d, [%%r%d+%%r%d] 0x%x\n", $inst, $dest_type, $dest_num, $rs1, $rs2, $imm_asi);
        }
    }
    else {
        if($imm_asi == $ILLIMM) {
            printf("%s %s%d, [%%r%d+0x%x] %%asi\n", $inst, $dest_type, $dest_num, $rs1, $imm);
        }
        else {
            printf("ta T_CHANGE_PRIV\n") if($opt{super} == 0);
            printf("wr %%r0, 0x%x, %%asi\n", $imm_asi); 
            printf("ta T_CHANGE_NONPRIV\n") if($opt{super} == 0);
            printf("%s %s%d, [%%r%d+0x%x] %%asi\n", $inst, $dest_type, $dest_num, $rs1, $imm);
        }
    }

}

sub cas() {
    my ($inst, $rs1, $rs2, $imm, $dest_type, $dest_num) = @_;
    if($imm == $ILLIMM) {
        printf("%s [%%r%d], %%r%d, %s%d\n", $inst, $rs1, $rs2, $dest_type, $dest_num);
    }
    else {
        my $tmp1 = &pick_tmp_reg();
        &add_regs($rs1, 0, $imm, $tmp1);
        printf("%s [%%r%d], %%r%d, %s%d\n", $inst, $tmp1, $rs2, $dest_type, $dest_num);
        &release_regs($tmp1);
    }
}

sub cas_alt() {
    my ($inst, $rs1, $rs2, $imm, $imm_asi, $dest_type, $dest_num) = @_;
    if($imm == $ILLIMM) {
        if($imm_asi == $ILLIMM) {
            printf("%s [%%r%d] %%asi, %%r%d, %s%d\n", $inst, $rs1, $rs2, $dest_type, $dest_num);
        }
        else {
            printf("%s [%%r%d] 0x%x, %%r%d, %s%d\n", $inst, $rs1, $imm_asi, $rs2, $dest_type, $dest_num);
        }
    }
    else {
        my $tmp1 = &pick_tmp_reg();
        &add_regs($rs1, 0, $imm, $tmp1);
        if($imm_asi == $ILLIMM) {
            printf("%s [%%r%d] %%asi, %%r%d, %s%d\n", $inst, $tmp1, $rs2, $dest_type, $dest_num);
        }
        else {
            printf("ta T_CHANGE_PRIV\n") if($opt{super} == 0);
            printf("wr %%r0, 0x%x, %%asi\n", $imm_asi); 
            printf("ta T_CHANGE_NONPRIV\n") if($opt{super} == 0);
            printf("%s [%%r%d] %%asi, %%r%d, %s%d\n", $inst, $tmp1, $rs2, $dest_type, $dest_num);
        }
        &release_regs($tmp1);
    }
}

# va  == net va
# imm == required imm in va
sub setup_addr_in_rs1_rs2() {
    my ($va, $rs1, $rs2, $imm, $err_ref) = @_; 
    my ($tmp_reg) = 0;

    $$err_ref = 0;
    if($imm != $ILLIMM) {
        if($rs1 != 0) {
            my ($remainder) = $va;
            if($imm & 0x1000)  {
                $imm |= 0xffffe000;
                $imm = ~$imm; $imm += 1; # 2's complement
                $remainder = $va + $imm;
            }
            else {
                $remainder = $va - $imm;
            }
            $tmp_reg = &pick_tmp_reg($rs1);
            &lock_regs($rs1);
            &setx($remainder, $tmp_reg, $rs1);
            &release_regs($tmp_reg);
            return $va;
        }
        else {
            return $imm;
        }
    }

    if($rs1 == 0 && $rs2 == 0) { 
        # no init needed, load will come from 0x0.
        return 0;
    }
    elsif($rs1 == 0) {
        $tmp_reg = &pick_tmp_reg($rs2);
        &setx($va, $tmp_reg, $rs2);
        &lock_regs($rs2);
        &release_regs($tmp_reg);
    }
    elsif($rs2 == 0) {
        $tmp_reg = &pick_tmp_reg($rs1);
        &setx($va, $tmp_reg, $rs1);
        &lock_regs($rs1);
        &release_regs($tmp_reg);
    }
    else {
        $tmp_reg = &pick_tmp_reg($rs1, $rs2);
        my ($half_addr) = 0;
        if($rs1 == $rs2) {
            if($va%2 != 0) {
                $$err_ref = 1; # Can't get this addr setup.
                return $va;
            }
            else {
                $half_addr = $va/2;
            }
        }
        else {
            $half_addr  = int($va/2 + $va/4);
        }
        my ($other_half) = $va - $half_addr;
        &setx($half_addr,  $tmp_reg, $rs1);
        &setx($other_half, $tmp_reg, $rs2);
        &lock_regs($rs1);
        &lock_regs($rs2) if($rs1 != $rs2);
        &release_regs($tmp_reg);
    }
    return $va;
}

sub gen_instr_sec() {

#   printf("SECTION .BIG_MAIN  TEXT_VA=0x30000000\n");
    printf("SECTION .BIG_MAIN  TEXT_VA=0x20000000\n");
    printf("attr_text {\n");
    printf("Name = .BIG_MAIN,\n");
#   printf("VA= 0x30000000,\n");
    printf("VA= 0x20000000,\n");
#   printf("RA= 0x120000000,\n");
    printf("RA= 0x130000000,\n");
#   printf("PA=ra2pa\(0x120000000,0\),\n");
    printf("PA=ra2pa\(0x130000000,0\),\n");
    printf("compressimage,\n");
#   printf("part_0_i_ctx_nonzero_ps0_tsb,\n");
    printf("part_0_ctx_nonzero_tsb_config_1,\n");
    printf("TTE_G=1, TTE_Context=0x44, TTE_V=1, TTE_Size=5, TTE_Size_Ptr=0, TTE_NFO=0,\n");
    printf("TTE_IE=0, TTE_Soft2=0, TTE_Diag=0, TTE_Soft=0,\n");
    printf("TTE_L=0, TTE_CP=1, TTE_CV=0, TTE_E=0, TTE_P=0, TTE_W=1\n");
    printf("}\n");
    if($opt{hyper}) {
        printf("attr_text {\n");
        printf("Name = .BIG_MAIN,\n");
        printf("hypervisor\n");
        printf("}\n");
    }
}

sub gen_data_sec() {
    my ($data_va, $last_data_va, $dataacc, $super) = @_;

    printf("SECTION RAND_DATA%s DATA_VA=0x%s\n", bigint2hex($data_va), bigint2hex($data_va));
    printf("attr_data {\n");
    printf("        Name = RAND_DATA%s,\n",bigint2hex($data_va));
    if($opt{hyper} && (($opt{inst_group} ne "asi") || 
                       (($opt{inst} ne "0x10") && ($opt{inst} ne "0x11") &&
                        ($opt{inst} ne "0x14") && ($opt{inst} ne "0x15") &&
                        ($opt{inst} ne "0x16") && ($opt{inst} ne "0x17") &&
                        ($opt{inst} ne "0x1c") && ($opt{inst} ne "0x1d") &&
                        ($opt{inst} ne "0x1e") && ($opt{inst} ne "0x1f") &&
                        ($opt{inst} ne "0x22") && ($opt{inst} ne "0x23") &&
                        ($opt{inst} ne "0x26") && ($opt{inst} ne "0x2e") &&
                        ($opt{inst} ne "0x2a") && ($opt{inst} ne "0x2b") &&
                        ($opt{inst} ne "0x18") && ($opt{inst} ne "0x19")))) {
        printf("hypervisor\n");
    }
    else {
        printf("        RA=0x%s,\n",bigint2hex($data_va));
        if($dataacc) {
            printf("        tsbonly,\n");
        }
        else {
            if(($data_va >> 39) & 0x1) { #if bit39 on va is set, make it equal to PA
                printf("        PA=0x%s,\n", bigint2hex($data_va));
            }
            else {
                printf("        PA=ra2pa\(0x%s,0\),\n", bigint2hex($data_va));
            }
        }
#       printf("        part_0_d_ctx_nonzero_ps0_tsb, \n");
        printf("        part_0_ctx_nonzero_tsb_config_0, \n");
        if((($opt{inst_group} eq "asi") && (($opt{inst} eq "0x4")  || ($opt{inst} eq "0xc")  ||
                                            ($opt{inst} eq "0x24") || ($opt{inst} eq "0x26") ||
                                            ($opt{inst} eq "0x2c") || ($opt{inst} eq "0x2e") ||
                                            ($opt{inst} eq "0x27") || ($opt{inst} eq "0x2f"))) || 
            (($opt{inst_group} eq "ldst") && ($opt{inst} eq "tl1"))) {
#           printf("    part_0_d_ctx_zero_ps0_tsb, \n");
            printf("    part_0_ctx_zero_tsb_config_0, \n");
        }
        if(($opt{inst_group} eq "asi") && (($opt{inst} eq "0x11") || ($opt{inst} eq "0x19") ||
                                           ($opt{inst} eq "0x81") || ($opt{inst} eq "0x83") ||
                                           ($opt{inst} eq "0x89") || ($opt{inst} eq "0x8b") ||
                                           ($opt{inst} eq "0x17") || ($opt{inst} eq "0x1f") ||
                                           ($opt{inst} eq "0x23") || ($opt{inst} eq "0x2b") ||
                                           ($opt{inst} eq "0xe3") || ($opt{inst} eq "0xeb") ||
                                           ($opt{inst} eq "0xf1") || ($opt{inst} eq "0xf9"))) {
            printf("TTE_Context=SCONTEXT\n");
        }
        else {
            printf("TTE_Context=PCONTEXT\n");
        } 
        printf("        TTE_G=1, TTE_Size=0x0, \n");
        if(($opt{inst_group} eq "asi") && (($opt{inst} eq "0x82") || ($opt{inst} eq "0x83") ||
                                           ($opt{inst} eq "0x8a") || ($opt{inst} eq "0x8b"))) {
            printf("TTE_NFO=1,\n");
        }
        else {
            printf("TTE_NFO=0,\n");
        }
        printf("        TTE_IE=0, TTE_Soft2=0, TTE_Diag=0, TTE_Soft=0,\n");
        printf("        TTE_L=0, TTE_CP=1, TTE_CV=0, TTE_E=0, "); 
        if(($dataacc && $opt{inst_group} ne "asi") || $opt{super}) {
            if(($opt{inst_group} eq "asi") && ($opt{inst} eq "0x10" || $opt{inst} eq "0x11" ||
                                               $opt{inst} eq "0x18" || $opt{inst} eq "0x19" ||
                                               $opt{inst} eq "0x22" || $opt{inst} eq "0x23" ||
                                               $opt{inst} eq "0x2a" || $opt{inst} eq "0x2b" ||
                                               $opt{inst} eq "0x80" || $opt{inst} eq "0x81" ||
                                               $opt{inst} eq "0x82" || $opt{inst} eq "0x83" ||
                                               $opt{inst} eq "0x88" || $opt{inst} eq "0x89" ||
                                               $opt{inst} eq "0x8a" || $opt{inst} eq "0x8b")) {
                printf("TTE_P=0, TTE_W=1,  TTE_V=1\n");
            }
            else {
                printf("TTE_P=1, TTE_W=1,  TTE_V=1\n");
            }
        }
        else {
            printf("TTE_P=0, TTE_W=1,  TTE_V=1\n");
        }
    }
    printf("        }\n");
    printf(".data\n");
    printf(".global data_sec%s\n",bigint2hex($data_va));
    printf("normal_addr_range_data%s:\n",bigint2hex($data_va));
    if($dataacc == 0) {
        for (my $i=0; $i<($last_data_va-$data_va)/4; $i++) { 
            printf(".word 0x%x\n", int(rand(0xffffffff)));
        }
    }
    else {
        printf(".skip %d\n", ($last_data_va-$data_va)); 
        printf(".byte 0x%x\n", int(rand(0xff)));
    }

    printf(".end\n");
}


sub gen_high_data_sec() {
    my ($dataacc, $super) = @_;

#   printf("SECTION RAND_DATAffffffffffffe000 DATA_VA=0xffffffffffffe000\n");
   printf("SECTION RAND_DATAffffffffff838000 DATA_VA=0xffffffffff838000\n");
    printf("attr_data {\n");
#   printf("        Name = RAND_DATAffffffffffffe000,\n");
    printf("        Name = RAND_DATAffffffffff838000,\n");
    if($opt{hyper}) {
        printf("hypervisor\n");
    }
    else {
#       printf("        RA=0x20010000,\n");
        printf("        RA=0x17f000000,\n");
        if($dataacc) {
            printf("        tsbonly,\n");
        }
        else {
#           printf("        PA=ra2pa\(0x20010000,0\),\n");
            printf("        PA=ra2pa\(0x17f000000,0\),\n");
        }
        if($super) {
#           printf("        part_0_d_ctx_nonzero_ps0_tsb, TTE_Context=PCONTEXT\n");
            printf("        part_0_ctx_nonzero_tsb_config_2, TTE_Context=PCONTEXT\n");
        }
        else {
#           printf("        part_0_d_ctx_nonzero_ps0_tsb, TTE_Context=PCONTEXT\n");
            printf("        part_0_ctx_nonzero_tsb_config_2, TTE_Context=PCONTEXT\n");
        }
        printf("        TTE_G=1, TTE_Size=0x0, TTE_NFO=0,\n");
        printf("        TTE_IE=0, TTE_Soft2=0, TTE_Diag=0, TTE_Soft=0,\n");
        printf("        TTE_L=0, TTE_CP=1, TTE_CV=0, TTE_E=0, ");
        if($dataacc) {
            if($opt{super}) {
                printf("TTE_P=0, TTE_W=1,  TTE_V=0\n");
            }
            else {
                printf("TTE_P=1, TTE_W=1,  TTE_V=1\n");
            }
        }
        else {
            printf("TTE_P=0, TTE_W=1,  TTE_V=1\n");
        }
    }
    printf("        }\n");
    printf(".data\n");
#   printf(".global data_secffffffffffffe000\n");
    printf(".global data_secffffffffff838000\n");
#   printf("normal_addr_range_dataffffffffffffe000:\n");
    printf("normal_addr_range_dataffffffffff838000:\n");
    if($dataacc == 0) {
        for (my $i=0; $i<8188/4; $i++) { 
            printf(".word 0x%x\n", int(rand(0xffffffff)));
        }
    }
    else {
        printf(".skip 8180\n");
        printf(".byte 0x%x\n", int(rand(0xff)));
    }

    printf(".end\n");
}

sub gen_scratch_mem_area() {
#   printf("SECTION SCRATCH_MEM DATA_VA=0x1ffffe000\n");
    printf("SECTION SCRATCH_MEM DATA_VA=0x17fff0000\n");
#    printf("attr_data {\n");
#    printf("        Name = SCRATCH_MEM,\n");
#    printf("        hypervisor\n");
#    printf("        }\n");
    printf("attr_data {\n");
    printf("        Name = SCRATCH_MEM,\n");
#   printf("        RA=0x1ffffe000,\n");
    printf("        RA=0x17fff0000,\n");
#   printf("        PA=ra2pa\(0x1ffffe000,0\),\n");
    printf("        PA=ra2pa\(0x17fff0000,0\),\n");
#   printf("        part_0_d_ctx_nonzero_ps0_tsb, \n");
    printf("        part_0_ctx_nonzero_tsb_config_1, \n");
#   printf("        part_0_d_ctx_zero_ps0_tsb, \n");
    printf("        part_0_ctx_zero_tsb_config_1, \n");
    printf("        TTE_Context=PCONTEXT\n");
    printf("        TTE_G=1, TTE_Size=0x0, TTE_NFO=0,\n");
    printf("        TTE_IE=0, TTE_Soft2=0, TTE_Diag=0, TTE_Soft=0,\n");
    printf("        TTE_L=0, TTE_CP=1, TTE_CV=0, TTE_E=0, ");
    printf("        TTE_P=0, TTE_W=1,  TTE_V=1\n");
    printf("        }\n");
    printf(".data\n");
    printf(".global scratch_mem\n");
    printf("scratch_mem:\n");
    for (my $i=0; $i<1024/4; $i++) { 
            printf(".word 0x%x\n", int(rand(0xffffffff)));
    }
}

sub setup_va_watchpoint() {
    my ($addr, $rd_en, $wr_en) = @_;

    if(($opt{enboff} == 0) && ($opt{hyper} == 0)) {
        printf("ta T_CHANGE_HPRIV\n")
    }
    elsif ($opt{enboff} && ($opt{super} == 0)) {
        printf("ta T_CHANGE_PRIV\n")
    }
    else {
        # hyper mode, or enboff.super mode requires nothing.
    }
    my $tmp_reg_lsu_ctl = &pick_tmp_reg();
    &load_alt("ldxa", 0, 0, $ILLIMM, 0x45, "%r", $tmp_reg_lsu_ctl);
    my $tmp1 = &pick_tmp_reg();
    my $tmp2 = &pick_tmp_reg();
    my $tmp_rst_mask = hex2bigint("0x000fffff");
    &setx($tmp_rst_mask, $tmp1, $tmp2);
    &and_regs($tmp2, $tmp_reg_lsu_ctl, $ILLIMM, $tmp_reg_lsu_ctl);

    my $ctl_reg_mask  = hex2bigint("0x7fe000000");
    $ctl_reg_mask |= ($wr_en << 23);
    $ctl_reg_mask |= ($rd_en << 24);
    &setx($ctl_reg_mask, $tmp1, $tmp2); 
    &or_regs($tmp_reg_lsu_ctl, $tmp2, $ILLIMM, $tmp_reg_lsu_ctl);
    &store_alt("stxa", "%r", $tmp_reg_lsu_ctl, 0, 0, $ILLIMM, 0x45);
 
    #now setup addr
    if($rd_en || $wr_en) { # only then address matters
        my $big_addr = Math::BigInt->new($addr);
        if(($big_addr & 0xfffff000) == 0x1000) {
            my $big_tmp = Math::BigInt->new(0xffffffff);
            $big_tmp <<= 32;
            $big_addr = $big_tmp | 0xffffe000 | $big_addr; 
         }
         &setx($big_addr, $tmp1, $tmp2);
         &mov(0, 0x38, $tmp1);
         &store_alt("stxa", "%r", $tmp2, $tmp1, 0, $ILLIMM, 0x58);
    }
    if(($opt{hyper} == 0) && ($opt{enboff} ==  0)) {
        printf("ta T_CHANGE_NONHPRIV\n")
    }
    elsif ($opt{enboff} && ($opt{super} == 0)) {
        printf("ta T_CHANGE_NONPRIV\n")
    }
    else {
        # hyper mode, or enboff.super mode requires nothing.
    }
    &release_regs($tmp_reg_lsu_ctl, $tmp1, $tmp2);
}

sub setup_tte_entry() {
    my ($addr) = @_;
    
    printf("!func: setup_tte_entry : 0x%x\n", $addr);

    my $err = 0;
    my $tmp1 = &pick_tmp_reg();
    my $tmp2 = &pick_tmp_reg();
    printf("ta T_CHANGE_PRIV\n") if($opt{super} == 0);
    &setx($addr, $tmp1, $tmp2);
    &load("ldub", $tmp2, 0, $ILLIMM, "%r", 0);
    &release_regs($tmp1, $tmp2);
    printf("ta T_CHANGE_NONPRIV\n") if($opt{super} == 0);
}

sub gen_all_ldf() {
    my ($ldf_inst, $next_addr_ref, $next_0addr_ref, $all_dest, $misalgn_offset, $vawatch, $preloadtlb) = @_;

    my $factor = 1;
       $factor = 2 if($ldf_inst eq "ldd" || $ldf_inst eq "ldda");

    my @fdest = ();
    if($all_dest) {
        @fdest = (0..31);
    }
    else {
        @fdest = (0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x1f);
    }

    my ($ldf_dest, $rs1, $rs2, $err);
    foreach $ldf_dest (@fdest) {                                        #pick dest freg
        my $dest = $ldf_dest*$factor;
        foreach $rs1 (@walking1_iregs) {                               #mem addr combination rs1
            my @rep_rs2 = (0, $rs1, 31);
            foreach $rs2 (@rep_rs2) {                           #mem addr combination rs2
                my ($imm) = $ILLIMM;
                my $setup_va = &setup_addr_in_rs1_rs2($$next_addr_ref+$misalgn_offset, $rs1, $rs2, $imm, \$err);
                if($err == 0) {
                    if($setup_va == ($$next_addr_ref+$misalgn_offset)) { 
                        $$next_addr_ref += (4*$factor); 
                    }
                    else {
                        push(@$next_0addr_ref, $setup_va);
                    }
                    if($misalgn_offset == 0 || $setup_va != 0) {
                        &setup_tte_entry($setup_va) if($preloadtlb);
                        &setup_va_watchpoint($setup_va, 1, 0) if($vawatch);
                        &load_reg($ldf_inst, $rs1, $rs2, $imm, 0x88, "%f", $dest);
                        &setup_va_watchpoint($setup_va, 0, 0) if($vawatch);
                        printf("\n");
                    }
                }
                &release_all_regs();
            }

            my @rep_imm13 = (0, 0x800, 0x1000, 0x1fc0);
            foreach my $imm (@rep_imm13) {                           #mem addr combination imm
                my $setup_va = &setup_addr_in_rs1_rs2($$next_addr_ref+$misalgn_offset, $rs1, $rs2, $imm+$misalgn_offset, \$err);
                if($err == 0) {
                    if($setup_va == ($$next_addr_ref+$misalgn_offset)) { 
                        $$next_addr_ref += (4*$factor); 
                    }
                    else {
                        push(@$next_0addr_ref, $setup_va);
                    }
                    if($misalgn_offset == 0 || $setup_va != 0) {
                        &setup_tte_entry($setup_va) if($preloadtlb);
                        &setup_va_watchpoint($setup_va, 1, 0) if($vawatch);
                        &load_reg($ldf_inst, $rs1, $rs2, $imm+$misalgn_offset, 0x88, "%f", $dest);
                        &setup_va_watchpoint($setup_va, 0, 0) if($vawatch);
                        printf("\n");
                    }
                }
                &release_all_regs();
            }
        }

        foreach $rs2 (@walking1_iregs) {                               #mem addr combination rs1
            my @rep_rs1 = (0, $rs2, 31);
            foreach $rs1 (@rep_rs1) {                           #mem addr combination rs2
                my ($imm) = $ILLIMM;
                my $setup_va = &setup_addr_in_rs1_rs2($$next_addr_ref+$misalgn_offset, $rs1, $rs2, $imm, \$err);
                if($err == 0) {
                    if($setup_va == ($$next_addr_ref+$misalgn_offset)) { 
                        $$next_addr_ref += (4*$factor); 
                    }
                    else {
                        push(@$next_0addr_ref, $setup_va);
                    }
                    if($misalgn_offset == 0 || $setup_va != 0) {
                        &setup_tte_entry($setup_va) if($preloadtlb);
                        &setup_va_watchpoint($setup_va, 1, 0) if($vawatch);
                        &load_reg($ldf_inst, $rs1, $rs2, $imm, 0x88, "%f", $dest);
                        &setup_va_watchpoint($setup_va, 0, 0) if($vawatch);
                        printf("\n");
                    }
                }
                &release_all_regs();
            }
        }

        foreach my $imm (@walking1_imm13) {                               #mem addr combination rs1
            my @rep_rs1 = (0, 31);
            foreach $rs1 (@rep_rs1) {                           #mem addr combination rs2
                if($rs1 == 0 && ($imm & 0x3f)) {
                    #skip
                }
                else {
                    my $setup_va = &setup_addr_in_rs1_rs2($$next_addr_ref+$misalgn_offset, $rs1, $rs2, $imm+$misalgn_offset, \$err);
                    if($err == 0) {
                        if($setup_va == ($$next_addr_ref+$misalgn_offset)) { 
                            $$next_addr_ref += (4*$factor); 
                        }
                        else {
                            push(@$next_0addr_ref, $setup_va);
                        }
                        if($misalgn_offset == 0 || $setup_va != 0) {
                            &setup_tte_entry($setup_va) if($preloadtlb);
                            &setup_va_watchpoint($setup_va, 1, 0) if($vawatch);
                            &load_reg($ldf_inst, $rs1, $rs2, $imm+$misalgn_offset, 0x88, "%f", $dest);
                            &setup_va_watchpoint($setup_va, 0, 0) if($vawatch);
                            printf("\n");
                        }
                    }
                    &release_all_regs();
                }
            }
        }
    }
}


sub gen_all_fbfcc() {
    my ($th_id, $inst, $inst_group) = @_;
    foreach my $taken (0..1) {
        foreach my $cc_combo (0..3) {
            my $setup_ok = &setup_fcc_for_fbpfcc($inst, $inst_group, $taken, 0, $cc_combo, $th_id);
            if($setup_ok) {
                foreach my $annul (0..1) {
                    foreach my $target (@imm22_targets) {
                        &gen_fbpfcc($inst, $inst_group, $annul, 0, 0, $cc_combo, $target, $taken, $th_id);
                    }
                }
            }
        }
    }
}

sub gen_all_fbpfcc() {
    my ($th_id, $inst, $inst_group) = @_;
    foreach my $taken (0..1) {
        foreach my $which_fcc (0..3) {
            foreach my $cc_combo (0..3) {
                my $setup_ok = &setup_fcc_for_fbpfcc($inst, $inst_group, $taken, $which_fcc, $cc_combo, $th_id);
                if($setup_ok) {
                    foreach my $ptn (0..1) {
                        foreach my $annul (0..1) {
                            foreach my $target (@imm19_targets) {
                                &gen_fbpfcc($inst, $inst_group, $annul, $ptn, $which_fcc, $cc_combo, $target, $taken, $th_id);
                            }
                        }
                    }
                }
            }
        }
    }
}

sub gen_fbpfcc() {
    my ($inst, $inst_group, $annul, $ptn, $which_fcc, $cc_combo, $target, $taken, $th_id) = @_;

    return if(($taken == 0) && ($opt{fpdis} == 0) && ($inst eq "fba"));
    return if(($taken == 1) && ($opt{fpdis} == 0) && ($inst eq "fbn"));

    my $target_limit = 0x200000;
    $target_limit = 0x40000 if($inst_group eq "fbpfcc");

    if($taken && ($opt{fpdis} == 0) && $target == 0) {
        #taken branch on self will be inf-loop
    }
    else {
        if($target & $target_limit) {
            printf("setx th%d_nxt_%s_fcc%x_combo%x_ptn%x_a%x_t%x_tgt%x, %%g2, %%g3\n", $th_id, $inst, $which_fcc, $cc_combo,  
                    $ptn, $annul, $taken, $target);
            printf("th%d_tgt_%s_fcc%x_combo%x_ptn%x_a%x_t%x_tgt%x:\n", $th_id, $inst, $which_fcc, $cc_combo, $ptn, $annul, $taken, $target);
            printf("jmp %%g3\n");
            printf("nop\n");
        }

        printf("th%d_nxt_%s_fcc%x_combo%x_ptn%x_a%x_t%x_tgt%x:\n", $th_id, $inst, $which_fcc, $cc_combo, $ptn, $annul, $taken, $target);
        printf("th%d_tgt_%s_fcc%x_combo%x_ptn%x_a%x_t%x_tgt%x:\n", $th_id, $inst, $which_fcc, $cc_combo, $ptn, $annul, $taken, $target) if($target == 0);
        if($target & $target_limit) {
            printf("add %%g3, 0xc, %%g3\n");
        }
        printf("%s", $inst);
        printf(",a")  if($annul);
        printf(",pt") if($ptn == 1 && ($inst_group eq "fbpfcc"));
        printf(",pn") if($ptn == 0 && ($inst_group eq "fbpfcc"));
        printf(" %%fcc%d, ", $which_fcc) if($inst_group eq "fbpfcc"); 
        printf(" th%d_tgt_%s_fcc%x_combo%x_ptn%x_a%x_t%x_tgt%x\n", $th_id, $inst, $which_fcc, $cc_combo, $ptn, $annul, $taken, $target);
        if($target == 1) {
            printf("th%d_tgt_%s_fcc%x_combo%x_ptn%x_a%x_t%x_tgt%x:\n", $th_id, $inst, $which_fcc, $cc_combo, $ptn, $annul, $taken, $target);
            printf("nop ! delay slot & branch target\n");
            printf("\n");
        }
        else {
            if(($inst eq "fba" || $inst eq "fbn") && $annul && ($opt{fpdis} == 0)) {
                #eat annuled instr(make it illegal)
                printf(".word 0x0\n");
            }
            elsif(($taken == 0) && $annul && ($opt{fpdis} == 0)) {
                #eat annuled instr(make it illegal)
                printf(".word 0x0\n");
            }
            else {
                printf("nop ! delay slot\n");
            }
            if(($target != 0) && (($target & $target_limit) == 0)) { 
                if(($target > 2) && (($taken == 0) || $opt{fpdis})) {
                    printf("call th%d_tgt_%s_fcc%x_combo%x_ptn%x_a%x_t%x_tgt%x\n", $th_id, $inst, $which_fcc, $cc_combo, $ptn, $annul, $taken, $target);
                    printf("nop\n");
                    printf(".skip %d\n", ($target-4)*4) if($target > 4);
                }
                else {
                    printf(".skip %d\n", ($target-2)*4) if($target > 2);
                }
                printf("th%d_tgt_%s_fcc%x_combo%x_ptn%x_a%x_t%x_tgt%x:\n", $th_id, $inst, $which_fcc, $cc_combo, $ptn, $annul, $taken, $target);
                printf("nop ! branch target\n");
            }
            printf("\n");
        }
    }
}

sub setup_fcc_for_fbpfcc() {
    my ($inst, $inst_group, $taken, $which_fcc, $cc_combo, $th_id) = @_;

    my $ext_on = 0;
    $ext_on = 1 if($inst_group eq "fbpfcc");

    my $value  = $cc_combo;
    my $cc_E   = 0;
    my $cc_L   = 1;
    my $cc_G   = 2;
    my $cc_U   = 3;

    if($inst eq "fba" || $inst eq "fbn") {
        # all cc-combos are ok
    }
    elsif($inst eq "fbu") {
        return 0 if(($taken == 1) && ($value != $cc_U));
        return 0 if(($taken == 0) && ($value == $cc_U));
    }
    elsif($inst eq "fbg") {
        return 0 if(($taken == 1) && ($value != $cc_G));
        return 0 if(($taken == 0) && ($value == $cc_G));
    }
    elsif($inst eq "fbug") {
        return 0 if(($taken == 1) && (($value != $cc_U) && ($value != $cc_G)));
        return 0 if(($taken == 0) && (($value == $cc_U) || ($value == $cc_G)));
    }
    elsif($inst eq "fbl") {
        return 0 if(($taken == 1) && ($value != $cc_L));
        return 0 if(($taken == 0) && ($value == $cc_L));
    }
    elsif($inst eq "fbul") {
        return 0 if(($taken == 1) && (($value != $cc_L) && ($value != $cc_U)));
        return 0 if(($taken == 0) && (($value == $cc_L) || ($value == $cc_U)));
    } 
    elsif($inst eq "fblg") {
        return 0 if(($taken == 1) && (($value != $cc_L) && ($value != $cc_G)));
        return 0 if(($taken == 0) && (($value == $cc_L) || ($value == $cc_G)));
    } 
    elsif($inst eq "fbne") {
        return 0 if(($taken == 1) && (($value != $cc_L) && ($value != $cc_G) && ($value != $cc_U)));
        return 0 if(($taken == 0) && (($value == $cc_L) || ($value == $cc_G) || ($value == $cc_U)));
    }
    elsif($inst eq "fbe") {
        return 0 if(($taken == 1) && ($value != $cc_E));
        return 0 if(($taken == 0) && ($value == $cc_E));
    }
    elsif($inst eq "fbue") {
        return 0 if(($taken == 1) && (($value != $cc_U) && ($value != $cc_E)));
        return 0 if(($taken == 0) && (($value == $cc_U) || ($value == $cc_E)));
    }
    elsif($inst eq "fbge") {
        return 0 if(($taken == 1) && (($value != $cc_G) && ($value != $cc_E)));
        return 0 if(($taken == 0) && (($value == $cc_G) || ($value == $cc_E)));
    }
    elsif($inst eq "fbuge") {
        return 0 if(($taken == 1) && (($value != $cc_U) && ($value != $cc_G) && ($value != $cc_E)));
        return 0 if(($taken == 0) && (($value == $cc_U) || ($value == $cc_G) || ($value == $cc_E)));
    }
    elsif($inst eq "fble") {
        return 0 if(($taken == 1) && (($value != $cc_E) && ($value != $cc_L)));
        return 0 if(($taken == 0) && (($value == $cc_E) || ($value == $cc_L)));
    }
    elsif($inst eq "fbule") {
        return 0 if(($taken == 1) && (($value != $cc_E) && ($value != $cc_L) && ($value != $cc_U)));
        return 0 if(($taken == 0) && (($value == $cc_E) || ($value == $cc_L) || ($value == $cc_U)));
    }
    elsif($inst eq "fbo") {
        return 0 if(($taken == 1) && (($value != $cc_E) && ($value != $cc_L) && ($value != $cc_G)));
        return 0 if(($taken == 0) && (($value == $cc_E) || ($value == $cc_L) || ($value == $cc_G)));
    }
    else {
        printf("ERROR: bad instruction type\n");
        return 0;
    }
    my $others = (~$value) & 0x3;

    if($which_fcc == 0) {
        &setup_xfsr_fcc($ext_on, $others, $others, $others, $value, $th_id);
    }
    if($which_fcc == 1) {
        &setup_xfsr_fcc($ext_on, $others, $others, $value, $others, $th_id);
    }
    if($which_fcc == 2) {
        &setup_xfsr_fcc($ext_on, $others, $value, $others, $others, $th_id);
    }
    if($which_fcc == 3) {
        &setup_xfsr_fcc($ext_on, $value,  $others, $others, $others, $th_id);
    }
    return 1;
}

sub setup_xfsr_fcc() {
    my ($ext_on, $fcc3, $fcc2, $fcc1, $fcc0, $th_id) = @_;

    my $big_value     = Math::BigInt->new(($fcc3<<4) | ($fcc2<<2) | ($fcc1));
    $big_value      <<=32 ;
    $big_value       |= ($fcc0<<10); 

    if($ext_on) {
        printf("stx %%fsr, [%%g0+%d]\n", $th_id*8);
    }
    else {
        printf("st  %%fsr, [%%g0+%d]\n", $th_id*4);
    }
    my $tmp_fsr = &pick_tmp_reg();
    if($ext_on) {
        printf("ldx [%%g0+%d], %%r%d\n", $th_id*8, $tmp_fsr);
    }
    else {
        printf("ld  [%%g0+%d], %%r%d\n", $th_id*4, $tmp_fsr);
    }
    my $tmp1 = &pick_tmp_reg();
    my $tmp2 = &pick_tmp_reg();
    &setx(0xfffff3ff, $tmp1, $tmp2);
    &and_regs($tmp_fsr, $tmp2, $ILLIMM, $tmp_fsr);
    &setx($big_value, $tmp1, $tmp2);
    &or_regs($tmp_fsr, $tmp2, $ILLIMM, $tmp_fsr);
    if($ext_on) {
        printf("stx %%r%d, [%%g0+%d]\n", $tmp_fsr, $th_id*8);
        printf("ldx [%%g0+%d], %%fsr\n", $th_id*8);
    }
    else {
        printf("st  %%r%d, [%%g0+%d]\n", $tmp_fsr, $th_id*4);
        printf("ld  [%%g0+%d], %%fsr\n", $th_id*4);
    }
    &release_regs($tmp_fsr, $tmp1, $tmp2);
}



sub gen_all_bicc() {
    my ($th_id, $inst, $inst_group) = @_;
    foreach my $taken (0..1) {
        my @cc_combo_range = (0..15);
        foreach my $cc_combo (@cc_combo_range) {
            my $setup_ok = &setup_cc_for_bpcc($inst, $inst_group, $taken, 0, $cc_combo, $th_id);
            if($setup_ok) {
                foreach my $annul (0..1) {
                    foreach my $target (@imm22_targets) {
                        &gen_bpcc($inst, $inst_group, $annul, 0, 0, $cc_combo, $target, $taken, $th_id);
                    }
                }
            }
        }
    }
}

sub gen_all_bpcc() {
    my ($th_id, $inst, $inst_group) = @_;

    foreach my $taken (0..1) {
        my @cc = (0,2);
           @cc = (1,3) if($opt{illinst});
        foreach my $which_cc (@cc) {
            my @cc_combo_range = (0..15);
            foreach my $cc_combo (@cc_combo_range) {
                my $setup_ok = &setup_cc_for_bpcc($inst, $inst_group, $taken, $which_cc, $cc_combo, $th_id);
                if($setup_ok) {
                    foreach my $ptn (0..1) {
                        foreach my $annul (0..1) {
                            foreach my $target (@imm19_targets) {
                                &gen_bpcc($inst, $inst_group, $annul, $ptn, $which_cc, $cc_combo, $target, $taken, $th_id);
                            }
                        }
                    }
                }
            }
        }
    }
}

sub gen_bpcc() {
    my ($inst, $inst_group, $annul, $ptn, $which_cc, $cc_combo, $target, $taken, $th_id) = @_;

    my %bpcc_cond = (
                   ba     => 0x8, bn     => 0x0, bne    => 0x9, be    => 0x1, bg     => 0xa,  
                   ble    => 0x2, bge    => 0xb, bl     => 0x3, bgu   => 0xc, bleu   => 0x4, 
                   bcc    => 0xd, bcs    => 0x5, bpos   => 0xe, bneg  => 0x6, bvc    => 0xf, 
                   bvs    => 0x7
                );

    return if(($taken == 0) && ($opt{illinst} == 0) && ($inst eq "ba"));
    return if(($taken == 1) && ($opt{illinst} == 0) && ($inst eq "bn"));

    my $target_limit = 0x200000;
    $target_limit = 0x40000 if($inst_group eq "bpcc");

    if($taken && ($opt{illinst} == 0) && $target == 0) {
        #taken branch on self will be inf-loop
    }
    else {
        if($target & $target_limit) {
            printf("setx th%d_nxt_%s_cc%x_combo%x_ptn%x_a%x_t%x_tgt%x, %%g2, %%g3\n", $th_id, $inst, $which_cc, $cc_combo,
                    $ptn, $annul, $taken, $target);
            printf("th%d_tgt_%s_cc%x_combo%x_ptn%x_a%x_t%x_tgt%x:\n", $th_id, $inst, $which_cc, $cc_combo, $ptn, $annul, $taken, $target);
            printf("jmp %%g3\n");
            printf("nop\n");
        }

        printf("th%d_nxt_%s_cc%x_combo%x_ptn%x_a%x_t%x_tgt%x:\n", $th_id, $inst, $which_cc, $cc_combo, $ptn, $annul, $taken, $target);
        printf("th%d_tgt_%s_cc%x_combo%x_ptn%x_a%x_t%x_tgt%x:\n", $th_id, $inst, $which_cc, $cc_combo, $ptn, $annul, $taken, $target) if($target == 0);
        if($target & $target_limit) {
            printf("add %%g3, 0xc, %%g3\n");
        }
        if($opt{illinst}) {
            my $inst_word = (($annul & 0x1)<<29) | (($bpcc_cond{$inst} & 0xf)<<25) | (0x1<<22) | (($which_cc & 0x3)<<20) |
                            (($ptn   & 0x1)<<19) | ($target & 0x7ffff);
            printf(".word 0x%x\n", $inst_word);
        }
        else {
            printf("%s", $inst);
            printf(",a")  if($annul);
            printf(",pt") if($ptn == 1 && ($inst_group eq "bpcc"));
            printf(",pn") if($ptn == 0 && ($inst_group eq "bpcc"));
            if($inst_group eq "bpcc") {
                printf(" %%icc, ") if($which_cc == 0); 
                printf(" %%xcc, ") if($which_cc == 2); 
            }
            printf(" th%d_tgt_%s_cc%x_combo%x_ptn%x_a%x_t%x_tgt%x\n", $th_id, $inst, $which_cc, $cc_combo, $ptn, $annul, $taken, $target);
        }
        if($target == 1) {
            printf("th%d_tgt_%s_cc%x_combo%x_ptn%x_a%x_t%x_tgt%x:\n", $th_id, $inst, $which_cc, $cc_combo, $ptn, $annul, $taken, $target);
            printf("nop ! delay slot & branch target\n");
            printf("\n");
        }
        else {
            if(($inst eq "ba" || $inst eq "bn") && $annul && ($opt{illinst} == 0)) {
                #eat annuled instr(make it illegal)
                printf(".word 0x0\n");
            }
            elsif(($taken == 0) && $annul && ($opt{illinst} == 0)) {
                #eat annuled instr(make it illegal)
                printf(".word 0x0\n");
            }
            else {
                printf("nop ! delay slot\n");
            }
            if(($target != 0) && (($target & $target_limit) == 0)) { 
                if(($target > 2) && (($taken == 0) || $opt{illinst})) {
                    printf("call th%d_tgt_%s_cc%x_combo%x_ptn%x_a%x_t%x_tgt%x\n", $th_id, $inst, $which_cc, $cc_combo, $ptn, $annul, $taken, $target);
                    printf("nop\n");
                    printf(".skip %d\n", ($target-4)*4) if($target > 4);
                }
                else {
                    printf(".skip %d\n", ($target-2)*4) if($target > 2);
                }
                printf("th%d_tgt_%s_cc%x_combo%x_ptn%x_a%x_t%x_tgt%x:\n", $th_id, $inst, $which_cc, $cc_combo, $ptn, $annul, $taken, $target);
                printf("nop ! branch target\n");
            }
            printf("\n");
        }
    }
}

sub setup_cc_for_bpcc() {
    my ($inst, $inst_group, $taken, $which_cc, $cc_combo, $th_id) = @_;

    my $ext_on = 0;
    $ext_on = 1 if($inst_group eq "bpcc");

    my $cc   = $cc_combo;
    my $cc_C = ($cc_combo & 0x1);
    my $cc_V = ($cc_combo & 0x2)>>1;
    my $cc_Z = ($cc_combo & 0x4)>>2;
    my $cc_N = ($cc_combo & 0x8)>>3;

    if($inst eq "ba" || $inst eq "bn") {
        # all cc combos are ok
    }
    elsif($inst eq "bne") {
        return 0 if (($taken == 1) && ($cc_Z == 1));
        return 0 if (($taken == 0) && ($cc_Z == 0));
    }
    elsif($inst eq "be") {
        return 0 if (($taken == 1) && ($cc_Z == 0));
        return 0 if (($taken == 0) && ($cc_Z == 1));
    }
    elsif($inst eq "bg") {
        return 0 if (($taken == 1) && ( ($cc_Z == 1) || 
                                        (($cc_N == 1) && ($cc_V == 0)) ||
                                        (($cc_N == 0) && ($cc_V == 1)) ) );
        return 0 if (($taken == 0) && ( ($cc_Z == 0) && 
                                        (($cc_N == 1) && ($cc_V == 1)) ||
                                        (($cc_N == 0) && ($cc_V == 0)) ) );
    }
    elsif($inst eq "ble") {
        return 0 if (($taken == 1) && ( ($cc_Z == 0) && 
                                        (($cc_N == 1) && ($cc_V == 1)) ||
                                        (($cc_N == 0) && ($cc_V == 0)) ) );
        return 0 if (($taken == 0) && ( ($cc_Z == 1) || 
                                        (($cc_N == 1) && ($cc_V == 0)) ||
                                        (($cc_N == 0) && ($cc_V == 1)) ) );
    }
    elsif($inst eq "bge") {
        return 0 if (($taken == 1) && ( (($cc_N == 1) && ($cc_V == 0)) ||
                                        (($cc_N == 0) && ($cc_V == 1)) ) );
        return 0 if (($taken == 0) && ( (($cc_N == 1) && ($cc_V == 1)) ||
                                        (($cc_N == 0) && ($cc_V == 0)) ) );
    }
    elsif($inst eq "bl") {
        return 0 if (($taken == 1) && ( (($cc_N == 1) && ($cc_V == 1)) ||
                                        (($cc_N == 0) && ($cc_V == 0)) ) );
        return 0 if (($taken == 0) && ( (($cc_N == 1) && ($cc_V == 0)) ||
                                        (($cc_N == 0) && ($cc_V == 1)) ) );
    }
    elsif($inst eq "bgu") {
        return 0 if (($taken == 1) && (($cc_C == 1) || ($cc_Z == 1)));
        return 0 if (($taken == 0) && (($cc_C == 0) && ($cc_Z == 0)));
    }
    elsif($inst eq "bleu") {
        return 0 if (($taken == 1) && (($cc_C == 0) && ($cc_Z == 0)));
        return 0 if (($taken == 0) && (($cc_C == 1) || ($cc_Z == 1)));
    }
    elsif($inst eq "bcc") {
        return 0 if (($taken == 1) && ($cc_C == 1));
        return 0 if (($taken == 0) && ($cc_C == 0));
    }
    elsif($inst eq "bcs") {
        return 0 if (($taken == 1) && ($cc_C == 0));
        return 0 if (($taken == 0) && ($cc_C == 1));
    }
    elsif($inst eq "bpos") {
        return 0 if (($taken == 1) && ($cc_N == 1));
        return 0 if (($taken == 0) && ($cc_N == 0));
    }
    elsif($inst eq "bneg") {
        return 0 if (($taken == 1) && ($cc_N == 0));
        return 0 if (($taken == 0) && ($cc_N == 1));
    }
    elsif($inst eq "bvc") {
        return 0 if (($taken == 1) && ($cc_V == 1));
        return 0 if (($taken == 0) && ($cc_V == 0));
    }
    elsif($inst eq "bvs") {
        return 0 if (($taken == 1) && ($cc_V == 0));
        return 0 if (($taken == 0) && ($cc_V == 1));
    }
    else {
        printf("ERROR: unknown instruction\n");
    }

    if($which_cc == 0 || $which_cc == 1) {
        &setup_cc($ext_on, ((~$cc)&0xf), $cc, $th_id);
    }
    if($which_cc == 2 || $which_cc == 3) {
        &setup_cc($ext_on, $cc, ((~$cc)&0xf), $th_id);
    }
    return 1;
}

sub setup_cc() {
    my ($ext_on, $xcc, $icc, $th_id) = @_;

    my $value = ($xcc<<4) | $icc;

    printf("wr %%g0, 0x%x, %%ccr\n", $value);
}

my @asi_lda_insts = ("ldsba", "ldsha", "ldswa", "lduba", "lduha", "lduwa", 
                    "ldxa",  "ldda",  "ldaf",  "lddaf", "ldqaf", "prefetcha");  

my @asi_sta_insts = ("stba", "stha",  "stwa",  "stxa", "stda", 
                    "staf", "stdaf", "stqaf", "casa", "casxa", "ldstuba", "swapa");  

my $big_va  = Math::BigInt->new(0xc0);
$big_va   <<=32;
#my @asi_va_addrs = (0x0);
my @asi_va_addrs = (0x0, $big_va);

my %asi_spc_traps = (
                       0x15 => ("P:casa:dataacc,P:casxa:dataacc"),   # Real_IO
                       0x1d => ("P:casa:dataacc,P:casxa:dataacc"),   # Real_IO_L

                       0x16 => ("N:stdaf:dataacc,N:lddaf:dataacc"),  # BLK_LOAD_P
                       0x17 => ("N:stdaf:dataacc,N:lddaf:dataacc"),  # BLK_LOAD_S
                       0x1e => ("N:stdaf:dataacc,N:lddaf:dataacc"),  # BLK_LOAD_P_L
                       0x1f => ("N:stdaf:dataacc,N:lddaf:dataacc"),  # BLK_LOAD_S_L

                       0x66 => ("N:stxa:dataacc,N:stdaf:dataacc,N:ldxa:dataacc"),   # ICACHE_INSTR
                       0x67 => ("N:stxa:dataacc,N:stdaf:dataacc,N:ldxa:dataacc"),   # ICACHE_TAG

                       0x24 => ("N:ldda:dataacc"),  # QUAD_LDDA
                       0x26 => ("N:ldda:dataacc"),  # QUAD_LDDA
                       0x2c => ("N:ldda:dataacc"),  # QUAD_LDDA
                       0x2e => ("N:ldda:dataacc"),  # QUAD_LDDA

                       0x45 => ("N:stxa:dataacc,N:ldxa:dataacc"),  # LSU_CTL
                       0x4c => ("N:stxa:dataacc,N:ldxa:dataacc"),  # SPARC_ERROR_STATUS

                       0x22 => ("N:stba:dataacc,N:stha:dataacc,N:stwa:dataacc,N:stxa:dataacc,N:stda:dataacc,N:ldda:dataacc"),  # BLK_INIT
                       0x23 => ("N:stba:dataacc,N:stha:dataacc,N:stwa:dataacc,N:stxa:dataacc,N:stda:dataacc,N:ldda:dataacc"),  # BLK_INIT
                       0x2a => ("N:stba:dataacc,N:stha:dataacc,N:stwa:dataacc,N:stxa:dataacc,N:stda:dataacc,N:ldda:dataacc"),  # BLK_INIT
                       0x2b => ("N:stba:dataacc,N:stha:dataacc,N:stwa:dataacc,N:stxa:dataacc,N:stda:dataacc,N:ldda:dataacc"),  # BLK_INIT
                       0xe2 => ("N:stba:dataacc,N:stha:dataacc,N:stwa:dataacc,N:stxa:dataacc,N:stda:dataacc,N:ldda:dataacc"),  # BLK_INIT
                       0xe3 => ("N:stba:dataacc,N:stha:dataacc,N:stwa:dataacc,N:stxa:dataacc,N:stda:dataacc,N:ldda:dataacc"),  # BLK_INIT
                       0xea => ("N:stba:dataacc,N:stha:dataacc,N:stwa:dataacc,N:stxa:dataacc,N:stda:dataacc,N:ldda:dataacc"),  # BLK_INIT
                       0xeb => ("N:stba:dataacc,N:stha:dataacc,N:stwa:dataacc,N:stxa:dataacc,N:stda:dataacc,N:ldda:dataacc"),  # BLK_INIT

                       0x82 => ("P:stba:dataacc,P:stha:dataacc,P:stwa:dataacc,P:stxa:dataacc,P:stda:dataacc,P:staf:dataacc," . 
                                "P:stdaf:dataacc,P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),           # NFO
                       0x83 => ("P:stba:dataacc,P:stha:dataacc,P:stwa:dataacc,P:stxa:dataacc,P:stda:dataacc,P:staf:dataacc," . 
                                "P:stdaf:dataacc,P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),           # NFO
                       0x8a => ("P:stba:dataacc,P:stha:dataacc,P:stwa:dataacc,P:stxa:dataacc,P:stda:dataacc,P:staf:dataacc," . 
                                "P:stdaf:dataacc,P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),           # NFO
                       0x8b => ("P:stba:dataacc,P:stha:dataacc,P:stwa:dataacc,P:stxa:dataacc,P:stda:dataacc,P:staf:dataacc," . 
                                "P:stdaf:dataacc,P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),           # NFO

                       0xf0 => ("N:stdaf:dataacc,N:lddaf:dataacc"),  # BLK_P
                       0xf1 => ("N:stdaf:dataacc,N:lddaf:dataacc"),  # BLK_S
                       0xf8 => ("N:stdaf:dataacc,N:lddaf:dataacc"),  # BLK_PL
                       0xf9 => ("N:stdaf:dataacc,N:lddaf:dataacc")   # BLK_SL
                    );

my %asi_io_space_traps = (
                       0x4  => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIN
                       0xc  => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIN
                       0x10 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x11 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUS
                       0x18 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x19 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUS
                       0x2a => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x2b => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUS
                       0x14 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # REAL_MEM
                       0x1c => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x80 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x81 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x82 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x83 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x88 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x89 => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x8a => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc"),   # AIUP
                       0x8b => ("P:casa:dataacc,P:casxa:dataacc,P:ldstuba:dataacc,P:swapa:dataacc")    # AIUP
                    );

my %asi_rbw = (
                   0x42 => ("stxa,stdaf"),  # ASI_BIST_CONTROL
                   0x43 => ("stxa,stdaf"),  # ASI_ERR_INJECT
                   0x44 => ("stxa,stdaf"),  # ASI_STRM_CTL
                   0x73 => ("stxa,stdaf"),  # ASI_SWVR_UDB_INTR_W
                   0x45 => ("stxa,stdaf")   # ASI_LSU_CTL_REG
              );

my %store_rbw_inst = (
                         stxa  => "ldxa",
                         stdaf => "ldda"
                     );

my %trap_decode = (
                       illinst => 0x10,
                       dataacc => 0x16
                  );

my %external_asi = (
                       0x4  => 1, 0xc  => 1, 0x10 => 1, 0x11 => 1, 0x18 => 1, 0x19 => 1, 
                       0x80 => 1, 0x81 => 1, 0x82 => 1, 0x83 => 1, 0x88 => 1, 0x89 => 1, 0x8a => 1, 0x8b => 1,
                       0x14 => 1, 0x15 => 1, 0x16 => 1, 0x17 => 1, 0x1c => 1, 0x1d => 1, 0x1e => 1, 0x1f => 1,
                       0x22 => 1, 0x23 => 1, 0x24 => 1, 0x26 => 1, 0x27 => 1, 0x2a => 1, 0x2b => 1, 0x2c => 1,
                       0x2e => 1, 0x2f => 1, 0xe2 => 1, 0xe3 => 1, 0xea => 1, 0xeb => 1, 0xf0 => 1, 0xf1 => 1,
                       0xf8 => 1, 0xf9 => 1
                   );


sub read_before_write() {
    my ($asi, $inst) = @_;
    my ($rbw_inst);
    if($opt{hyper} || ($opt{super} && $opt{enboff})) {
    }
    else {
        return 0;
    }
    foreach my $rbw (split(/,/, $asi_rbw{$asi})) {
        ($rbw_inst) = split(/:/, $rbw);
        if($rbw_inst eq $inst) {
            return 1;
        }
    }
    return 0;
}

sub enable_trap() {
    my ($asi, $inst) = @_;
    my $neg_dec_vld = 1;
    my ($pos,$trap_inst,$trap_type);
    foreach my $traps (split(/,/, $asi_spc_traps{$asi})) {
        ($pos,$trap_inst,$trap_type) = split(/:/, $traps);
        if($pos eq "P") {
            if($trap_inst eq $inst) {
                &enable_trap_type($trap_type);
                return 1;
            }
        }
        else {
            if($trap_inst eq $inst) {
                $neg_dec_vld = 0;
            }
        }
    }
    if($pos eq "N" && $neg_dec_vld) {
        &enable_trap_type($trap_type);
        return 1;
    }
    return 0;
}


sub enable_io_space_traps() {
    my ($asi, $inst) = @_;
    my $neg_dec_vld = 1;
    my ($pos,$trap_inst,$trap_type);

    foreach my $traps (split(/,/, $asi_io_space_traps{$asi})) {
        ($pos,$trap_inst,$trap_type) = split(/:/, $traps);
        if($pos eq "P") {
            if($trap_inst eq $inst) {
                &enable_trap_type($trap_type);
                return 1;
            }
        }
        else {
            if($trap_inst eq $inst) {
                $neg_dec_vld = 0;
            }
        }
    }
    if($pos eq "N" && $neg_dec_vld) {
        &enable_trap_type($trap_type);
        return 1;
    }
    return 0;

}

sub enable_trap_type() {
    my ($trap) = @_;
    printf("set 0x%x, %%o0\n", $trap_decode{$trap});
    printf("set  0xff, %%o1\n");
    if($opt{enboff}) {
        printf("ta  T_TRAP_EN_N_TIMES\n");
    }
    else {
        printf("ta  T_HTRAP_EN_N_TIMES\n");
    }
}

sub gen_all_asi_insts() {
    my ($th_id, $asi_str) = @_;
    my $asi = hex($asi_str);

    &lock_regs(16);
    my $tmp1 = &pick_tmp_reg();
    my $tmp2 = &pick_tmp_reg();
    my $tmp3 = &pick_tmp_reg();
    
    &setx_label("scratch_mem", $tmp1, $tmp2);
    printf("ldx [%%r%d+%d], %%r16\n", $tmp2, ($th_id*2*64));
    printf("ldx [%%r%d+%d], %%r17\n", $tmp2, ($th_id*2*64)+8);
    for(my $i=0; $i<8; $i++) {
        printf("ldd [%%r%d+%d], %%f%d\n", $tmp2, ((($th_id*2)+1)*64+($i*8)), (16+$i*2));
    }
    if($asi == 0x4c) {
        &setx_label("0xffffffffffffffff", $tmp1, 16);
    }

    foreach my $va (@asi_va_addrs) {
        if($va >> 39) { # this is an io space.
            &setx($va, $tmp1, $tmp3);
            printf("ta T_CHANGE_PRIV\n") if($opt{super} == 0);
            printf("wr %%g0, 0x14, %%asi\n");
            printf("stxa %%r16, [%%r%d+%d] %asi\n", $tmp3, ($th_id*2*64));
            printf("stxa %%r17, [%%r%d+%d] %asi\n", $tmp3, ($th_id*2*64)+8);
            for(my $i=0; $i<8; $i++) {
                printf("stda %%f%d, [%%r%d+%d] %asi\n", (16+$i*2), $tmp3, ((($th_id*2)+1)*64+($i*8)));
            }
            printf("ta T_CHANGE_NONPRIV\n") if($opt{super} == 0);
        }
    }
    
    my $va_offset = 0;
    $va_offset = ($th_id*2*64) if($external_asi{$asi});

    my @asi_modes = ("normal",   "redbit_on",          "ambit_on",          "redbit_on_ambit_on", 
                     "oor_addr", "oor_addr_redbit_on", "oor_addr_ambit_on", "oor_addr_redbit_on_ambit_on", 
                     "dtlb_off", "dtlb_off_redbit_on", "dtlb_off_ambit_on", "dtlb_off_redbit_on_ambit_on", 
                     "oor_addr_dtlb_off", "oor_addr_dtlb_off_redbit_on", "oor_addr_dtlb_off_ambit_on", 
                     "oor_addr_dtlb_off_redbit_on_ambit_on");
    #my @asi_modes = ("normal",   "redbit_on", "oor_addr", "oor_addr_redbit_on",  "dtlb_off", "dtlb_off_redbit_on", 
    #                 "oor_addr_dtlb_off", "oor_addr_dtlb_off_redbit_on");
    foreach my $asi_mode (@asi_modes) {
        if($asi_mode =~ /dtlb_off/) {
            printf("mov 0x1, %%o7\n");
            printf("ta T_HTRAP_INST0\n"); 
        }
        else {
            printf("mov 0x2, %%o7\n");
            printf("ta T_HTRAP_INST0\n"); 
        }
        if($asi_mode =~ /redbit_on/) {
            printf("mov 0x8, %%o7\n");
            printf("ta T_HTRAP_INST0\n"); 
        }
        else {
            printf("mov 0x4, %%o7\n");
            printf("ta T_HTRAP_INST0\n"); 
        }
        if($asi_mode =~ /ambit_on/) {
            printf("!turn on AM bit\n");
            printf("ta T_CHANGE_PRIV\n") if($opt{super} == 0);
            printf("rdpr %%pstate, %%r%d\n", $tmp1);
            printf("or %%r%d, 0x8, %%r%d\n", $tmp1, $tmp1);
            printf("wrpr %%r%d, 0x0, %%pstate\n", $tmp1);
            printf("ta T_CHANGE_NONPRIV\n") if($opt{super} == 0);
        }
        else {
            printf("!turn off AM bit\n");
            printf("ta T_CHANGE_PRIV\n") if($opt{super} == 0);
            printf("rdpr %%pstate, %%r%d\n", $tmp1);
            printf("or %%r%d, 0x8, %%r%d\n", $tmp1, $tmp1);
            printf("wrpr %%r%d, 0x8, %%pstate\n", $tmp1);
            printf("ta T_CHANGE_NONPRIV\n") if($opt{super} == 0);
        }
        my $oor_va = 0x0;
        if($asi_mode =~ /oor_addr/) {
            $oor_va  = Math::BigInt->new(0x10000);
            $oor_va   <<=32;
        }
        foreach my $va (@asi_va_addrs) {
            &setx($va|$oor_va, $tmp2, $tmp1);
            foreach my $stinst (@asi_sta_insts) {
                my $trap_enabled = 0;
                if($stinst eq "stqaf") {
                    $trap_enabled = &enable_trap_type("illinst");
                }
                else {
                    $trap_enabled = &enable_trap($asi, $stinst) if($opt{seltrap});
                }
                if($trap_enabled == 0 && (($va>>39)&0x1)) {
                    $trap_enabled = &enable_io_space_traps($asi, $stinst);
                }
                if($external_asi{$asi} == undef && $stinst eq "stdaf") {
                    #skip this HACK
                }
                else {
                my $inst = $stinst;
                my $src_type = "%r";
                $src_type = "%f" if($stinst eq "staf" || $stinst eq "stdaf" || $stinst eq "stqaf");
                $src_type = ""   if($stinst eq "prefetcha");
                $inst =~ s/(.*)f$/$1/g if($stinst eq "staf" || $stinst eq "stdaf" || $stinst eq "stqaf");

                if($inst eq "casa" || $inst eq "casxa") {
                    &cas_alt($inst, $tmp1, 16, $va_offset, $asi, $src_type, 16);
                }
                elsif($inst eq "ldstuba" || $inst eq "swapa") {
                    &load_alt($inst, $tmp1, 0, $va_offset, $asi, $src_type, 16);
                }
                else {
                    &load_alt($store_rbw_inst{$stinst}, $tmp1, 0, $va_offset, $asi, $src_type, 16) if(&read_before_write($asi, $stinst));
                    &store_alt($inst, $src_type, 16, $tmp1, 0, $va_offset, $asi);
                }
                } #end HACK
            }
        }
        foreach my $va (@asi_va_addrs) {
            &setx($va|$oor_va, $tmp2, $tmp1);
            foreach my $ldinst (@asi_lda_insts) {
                if($ldinst eq "ldqaf") {
                    &enable_trap_type("illinst");
                }
                else {
                    &enable_trap($asi, $ldinst);
                }
                if($external_asi{$asi} == undef && $ldinst eq "lddaf") {
                    #skip this HACK
                }
                else {
                my $inst = $ldinst;
                my $dest_type = "%r";
                $dest_type = "%f" if($ldinst eq "ldaf" || $ldinst eq "lddaf" || $ldinst eq "ldqaf");
                $dest_type = ""   if($ldinst eq "prefetcha");
                $inst =~ s/(.*)f$/$1/g if($ldinst eq "ldaf" || $ldinst eq "lddaf" || $ldinst eq "ldqaf");
                &load_alt($inst, $tmp1, 0, $va_offset, $asi, $dest_type, 16);
                } #end HACK
            }
        }
    }
    &release_regs(16,$tmp1,$tmp2,$tmp3);
}

sub gen_required_data_sections() {
    if($opt{inst_group} eq "asi" || $opt{inst_group} eq "ldst") {
        foreach my $va (@asi_va_addrs) {
            &gen_data_sec($va, $va+1024, $opt{dataacc}, $opt{super}) if($va != 0x0);
        }
    }
}

sub hyp_helper_apis() {
    my ($tmp1, $tmp2) = (1,2);
    printf("SECTION .HTRAPS\n");
    printf(".text\n");
    printf(".global hyp_api\n");
    printf("hyp_api:\n");
    printf("and  %%o7, 0x1, %%o6\n");
    printf("brnz %%o6, turnoff_dtlb\n");
    printf("nop\n");
    printf("and  %%o7, 0x2, %%o6\n");
    printf("brnz %%o6, turnon_dtlb\n");
    printf("nop\n");
    printf("and  %%o7, 0x4, %%o6\n");
    printf("brnz %%o6, turnoff_redbit\n");
    printf("nop\n");
    printf("and  %%o7, 0x8, %%o6\n");
    printf("brnz %%o6, turnon_redbit\n");
    printf("nop\n");
    printf("ba api_done\n");
    printf("nop\n");

    printf("turnoff_dtlb:\n");
    printf("ldxa [%%g0] 0x45, %%r%d\n", $tmp1);
    printf("sllx %%r%d, 60, %%r%d\n", $tmp1, $tmp2);
    printf("srlx %%r%d, 63, %%r%d\n", $tmp2, $tmp2);
    printf("sllx %%r%d,  3, %%r%d\n", $tmp2, $tmp2);
    printf("xor  %%r%d,  %%r%d, %%r%d\n", $tmp1, $tmp2, $tmp1);
    printf("stxa %%r%d, [%%g0] 0x45\n", $tmp1);
    printf("ba api_done\n");
    printf("nop\n");

    printf("turnon_dtlb:\n");
    printf("ldxa [%%g0] 0x45, %%r%d\n", $tmp1);
    printf("or  %%r%d,  0x8, %%r%d\n", $tmp1, $tmp1);
    printf("stxa %%r%d, [%%g0] 0x45\n", $tmp1);
    printf("ba api_done\n");
    printf("nop\n");

    printf("turnon_redbit:\n");
    printf("rdhpr %%hpstate, %%r%d\n", $tmp1);
    printf("or %%r%d, 0x20, %%r%d\n", $tmp1, $tmp1);
    printf("wrhpr %%r%d, 0x0, %%hpstate\n", $tmp1);
    printf("ba api_done\n");
    printf("nop\n");

    printf("turnoff_redbit:\n");
    printf("rdhpr %%hpstate, %%r%d\n", $tmp1);
    printf("sllx %%r%d, 58, %%r%d\n", $tmp1, $tmp2);
    printf("srlx %%r%d, 63, %%r%d\n", $tmp2, $tmp2);
    printf("sllx %%r%d,  5, %%r%d\n", $tmp2, $tmp2);
    printf("wrhpr %%r%d, %%r%d, %%hpstate\n", $tmp1, $tmp2);
    printf("ba api_done\n");
    printf("nop\n");

    printf("api_done:\n");
    printf("done\n");
}


my @asi_ld_insts = ("ldsb", "ldsh", "ldsw", "ldub", "lduh", "lduw", 
                    "ldx",  "ldd",  "ldf",  "lddf", "ldqf", "prefetch");  

my @asi_st_insts = ("stb", "sth",  "stw",  "stx", "std", 
                    "stf", "stdf", "stqf", "cas", "casx", "ldstub", "swap");  

sub gen_all_ldst_insts() {
    my ($th_id) = @_;

    &lock_regs(16);
    my $tmp1 = &pick_tmp_reg();
    my $tmp2 = &pick_tmp_reg();
    my $tmp3 = &pick_tmp_reg();
    
    &setx_label("scratch_mem", $tmp1, $tmp2);
    printf("ldx [%%r%d+%d], %%r16\n", $tmp2, ($th_id*2*64));
    printf("ldx [%%r%d+%d], %%r17\n", $tmp2, ($th_id*2*64)+8);
    for(my $i=0; $i<8; $i++) {
        printf("ldd [%%r%d+%d], %%f%d\n", $tmp2, ((($th_id*2)+1)*64+($i*8)), (16+$i*2));
    }

    foreach my $va (@asi_va_addrs) {
        if($va >> 39) { # this is an io space.
            &setx($va, $tmp1, $tmp3);
            printf("ta T_CHANGE_PRIV\n") if($opt{super} == 0);
            printf("wr %%g0, 0x14, %%asi\n");
            printf("stxa %%r16, [%%r%d+%d] %asi\n", $tmp3, ($th_id*2*64));
            printf("stxa %%r17, [%%r%d+%d] %asi\n", $tmp3, ($th_id*2*64)+8);
            for(my $i=0; $i<8; $i++) {
                printf("stda %%f%d, [%%r%d+%d] %asi\n", (16+$i*2), $tmp3, ((($th_id*2)+1)*64+($i*8)));
            }
            printf("ta T_CHANGE_NONPRIV\n") if($opt{super} == 0);
        }
    }
    
    my $va_offset = ($th_id*2*64);

    my @asi_modes = ("normal",   "redbit_on",          "ambit_on",          "redbit_on_ambit_on", 
                     "oor_addr", "oor_addr_redbit_on", "oor_addr_ambit_on", "oor_addr_redbit_on_ambit_on", 
                     "dtlb_off", "dtlb_off_redbit_on", "dtlb_off_ambit_on", "dtlb_off_redbit_on_ambit_on", 
                     "oor_addr_dtlb_off", "oor_addr_dtlb_off_redbit_on", "oor_addr_dtlb_off_ambit_on", 
                     "oor_addr_dtlb_off_redbit_on_ambit_on");
    #my @asi_modes = ("normal",   "redbit_on", "oor_addr", "oor_addr_redbit_on",  "dtlb_off", "dtlb_off_redbit_on", 
    #                 "oor_addr_dtlb_off", "oor_addr_dtlb_off_redbit_on");
    foreach my $asi_mode (@asi_modes) {
        if($asi_mode =~ /dtlb_off/) {
            printf("mov 0x1, %%o7\n");
            printf("ta T_HTRAP_INST0\n"); 
        }
        else {
            printf("mov 0x2, %%o7\n");
            printf("ta T_HTRAP_INST0\n"); 
        }
        if($asi_mode =~ /redbit_on/) {
            printf("mov 0x8, %%o7\n");
            printf("ta T_HTRAP_INST0\n"); 
        }
        else {
            printf("mov 0x4, %%o7\n");
            printf("ta T_HTRAP_INST0\n"); 
        }
        if($asi_mode =~ /ambit_on/) {
            printf("!turn on AM bit\n");
            printf("ta T_CHANGE_PRIV\n") if($opt{super} == 0);
            printf("rdpr %%pstate, %%r%d\n", $tmp1);
            printf("or %%r%d, 0x8, %%r%d\n", $tmp1, $tmp1);
            printf("wrpr %%r%d, 0x0, %%pstate\n", $tmp1);
            printf("ta T_CHANGE_NONPRIV\n") if($opt{super} == 0);
        }
        else {
            printf("!turn off AM bit\n");
            printf("ta T_CHANGE_PRIV\n") if($opt{super} == 0);
            printf("rdpr %%pstate, %%r%d\n", $tmp1);
            printf("or %%r%d, 0x8, %%r%d\n", $tmp1, $tmp1);
            printf("wrpr %%r%d, 0x8, %%pstate\n", $tmp1);
            printf("ta T_CHANGE_NONPRIV\n") if($opt{super} == 0);
        }
        my $oor_va = 0x0;
        if($asi_mode =~ /oor_addr/) {
            $oor_va  = Math::BigInt->new(0x10000);
            $oor_va   <<=32;
        }
        foreach my $va (@asi_va_addrs) {
            &setx($va|$oor_va, $tmp2, $tmp1);
            foreach my $stinst (@asi_st_insts) {
                my $trap_enabled = 0;
                if($stinst eq "stqf") {
                    $trap_enabled = &enable_trap_type("illinst");
                }
                if($trap_enabled == 0 && (($va>>39)&0x1)) {
                    if($stinst eq "cas" || $stinst eq "casx" || 
                       $stinst eq "ldstub" || $stinst eq "swap") {
                        $trap_enabled = &enable_trap_type("dataacc");
                    }
                }
                my $inst = $stinst;
                my $src_type = "%r";
                $src_type = "%f" if($stinst eq "stf" || $stinst eq "stdf" || $stinst eq "stqf");
                $src_type = ""   if($stinst eq "prefetch");
                $inst =~ s/(.*)f$/$1/g if($stinst eq "stf" || $stinst eq "stdf" || $stinst eq "stqf");

                if($inst eq "cas" || $inst eq "casx") {
                    &cas($inst, $tmp1, 16, $va_offset, $src_type, 16);
                }
                elsif($inst eq "ldstub" || $inst eq "swap") {
                    &load($inst, $tmp1, 0, $va_offset, $src_type, 16);
                }
                else {
                    &store($inst, $src_type, 16, $tmp1, 0, $va_offset);
                }
            }
        }
        foreach my $va (@asi_va_addrs) {
            &setx($va|$oor_va, $tmp2, $tmp1);
            foreach my $ldinst (@asi_ld_insts) {
                if($ldinst eq "ldqf") {
                    &enable_trap_type("illinst");
                }
                my $inst = $ldinst;
                my $dest_type = "%r";
                $dest_type = "%f" if($ldinst eq "ldf" || $ldinst eq "lddf" || $ldinst eq "ldqf");
                $dest_type = ""   if($ldinst eq "prefetch");
                $inst =~ s/(.*)f$/$1/g if($ldinst eq "ldf" || $ldinst eq "lddf" || $ldinst eq "ldqf");
                &load($inst, $tmp1, 0, $va_offset, $dest_type, 16);
            }
        }
    }
    &release_regs(16,$tmp1,$tmp2,$tmp3);
}

1;
