stella/src/emucore/Thumbulator.cxx

2090 lines
44 KiB
C++
Raw Normal View History

//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2011 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
//============================================================================
// This class provides Thumb emulation code ("Thumbulator")
// by David Welch (dwelch@dwelch.com)
// Modified by Fred Quimby
// Code is public domain and used with the author's consent
//============================================================================
#ifdef THUMB_SUPPORT
#include "bspf.hxx"
#include "Thumbulator.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Thumbulator::Thumbulator(uInt16* rom_ptr, uInt16* ram_ptr)
: rom(rom_ptr),
ram(ram_ptr),
copydata(0),
DBUG(0), // dump detailed execution trace
DISS(0) // dump Thumb instruction trace
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Thumbulator::~Thumbulator()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Thumbulator::run( void ) throw(const string&)
{
reset();
for(;;)
{
if (execute()) break;
if (instructions > 500000) // way more than would otherwise be possible
{
DISS=1; // dump instructions
DBUG=1;
}
if (instructions > 501000) // Stop dumping, quit executing
{
DISS=0;
DBUG=0;
throw "instructions > 501000";
}
}
//dump_counters();
return statusMsg.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* msg)
throw(const string&)
{
statusMsg << "Thumb ARM emulation fatal error: " << endl
<< opcode << "(" << HEX8 << v1 << "), " << msg << endl;
dump_regs();
throw statusMsg.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, uInt32 v2,
const char* msg)
throw(const string&)
{
statusMsg << "Thumb ARM emulation fatal error: " << endl
<< opcode << "(" << HEX8 << v1 << "," << v2 << "), " << msg << endl;
dump_regs();
throw statusMsg.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::dump_counters ( void )
{
cout << endl << endl
<< "instructions " << instructions << endl
<< "fetches " << fetches << endl
<< "reads " << reads << endl
<< "writes " << writes << endl
<< "memcycles " << (fetches+reads+writes) << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::dump_regs( void )
{
for (int cnt = 1; cnt < 14; cnt++)
{
statusMsg << "R" << cnt << " = " << HEX8 << reg_sys[cnt-1] << " ";
if(cnt % 4 == 0) statusMsg << endl;
}
statusMsg << endl
<< "SP = " << HEX8 << reg_svc[13] << " "
<< "LR = " << HEX8 << reg_svc[14] << " "
<< "PC = " << HEX8 << reg_sys[15] << " "
<< endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Thumbulator::fetch16 ( uInt32 addr )
{
uInt32 data;
fetches++;
if(DBUG)
statusMsg << "fetch16(" << HEX8 << addr << ")=";
switch(addr&0xF0000000)
{
case 0x00000000: //ROM
addr &= ROMADDMASK;
if(addr<0x50)
fatalError("fetch16", addr, "abort");
addr>>=1;
#ifdef __BIG_ENDIAN__
data=((rom[addr]>>8)|(rom[addr]<<8))&0xffff;
#else
data=rom[addr];
#endif
if(DBUG)
statusMsg << HEX4 << data << endl;
return(data);
case 0x40000000: //RAM
addr &= RAMADDMASK;
addr>>=1;
#ifdef __BIG_ENDIAN__
data=((ram[addr]>>8)|(ram[addr]<<8))&0xffff;
#else
data=ram[addr];
#endif
if(DBUG)
statusMsg << HEX4 << data << endl;
return(data);
}
return fatalError("fetch16", addr, "abort");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Thumbulator::fetch32 ( uInt32 addr )
{
uInt32 data;
if(DBUG)
statusMsg << "fetch32(" << HEX8 << addr << ")=";
switch(addr&0xF0000000)
{
case 0x00000000: //ROM
if(addr<0x50)
{
data=read32(addr);
if(DBUG)
statusMsg << HEX8 << data << endl;
if(addr==0x00000000) return(data);
if(addr==0x00000004) return(data);
fatalError("fetch32", addr, "abort");
}
case 0x40000000: //RAM
data =fetch16(addr+2);
data<<=16;
data|=fetch16(addr+0);
if(DBUG)
statusMsg << HEX8 << data << endl;;
return(data);
}
return fatalError("fetch32", addr, "abort");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::write16 ( uInt32 addr, uInt32 data )
{
if((addr>0x40001fff)&&(addr<0x50000000))
fatalError("write16", addr, "abort - out of range");
else if((addr>0x40000028)&&(addr<0x40000c00))
fatalError("write16", addr, "to bankswitch code area");
if(addr&1)
fatalError("write16", addr, "abort - misaligned");
writes++;
if(DBUG)
statusMsg << "write16(" << HEX8 << addr << "," << HEX8 << data << ")" << endl;
switch(addr&0xF0000000)
{
case 0x40000000: //RAM
addr&=RAMADDMASK;
addr>>=1;
#ifdef __BIG_ENDIAN__
ram[addr]=(((data&0xFFFF)>>8)|((data&0xffff)<<8))&0xffff;
#else
ram[addr]=data&0xFFFF;
#endif
return;
}
fatalError("write16", addr, data, "abort");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::write32 ( uInt32 addr, uInt32 data )
{
if(addr&3)
fatalError("write32", addr, "abort - misaligned");
if(DBUG)
statusMsg << "write32(" << HEX8 << addr << "," << HEX8 << data << ")" << endl;
switch(addr&0xF0000000)
{
case 0xF0000000: //halt
dump_counters();
throw "HALT";// exit(0);
case 0xE0000000: //periph
switch(addr)
{
case 0xE0000000:
if(DISS) statusMsg << "uart: [";
statusMsg << char(data&0xFF);
if(DISS) statusMsg << "]" << endl;
break;
}
return;
case 0xD0000000: //debug
statusMsg << "[" << HEX8 << read_register(14) << "]["
<< addr << "] " << data << endl;
return;
case 0x40000000: //RAM
write16(addr+0,(data>> 0)&0xFFFF);
write16(addr+2,(data>>16)&0xFFFF);
return;
}
fatalError("write32", addr, data, "abort");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Thumbulator::read16 ( uInt32 addr )
{
uInt32 data;
if((addr>0x40001fff)&&(addr<0x50000000))
fatalError("read16", addr, "abort - out of range");
else if((addr>0x7fff)&&(addr<0x10000000))
fatalError("read16", addr, "abort - out of range");
if(addr&1)
fatalError("read16", addr, "abort - misaligned");
reads++;
if(DBUG)
statusMsg << "read16(" << HEX8 << addr << ")=";
switch(addr&0xF0000000)
{
case 0x00000000: //ROM
addr&=ROMADDMASK;
addr>>=1;
#ifdef __BIG_ENDIAN__
data=((rom[addr]>>8)|(rom[addr]<<8))&0xffff;
#else
data=rom[addr];
#endif
if(DBUG)
statusMsg << HEX4 << data << endl;
return(data);
case 0x40000000: //RAM
addr&=RAMADDMASK;
addr>>=1;
#ifdef __BIG_ENDIAN__
data=((ram[addr]>>8)|(ram[addr]<<8))&0xffff;
#else
data=ram[addr];
#endif
if(DBUG)
statusMsg << HEX4 << data << endl;
return(data);
}
return fatalError("read16", addr, "abort");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Thumbulator::read32 ( uInt32 addr )
{
if(addr&3)
fatalError("read32", addr, "abort - misaligned");
if(DBUG)
statusMsg << "read32(" << HEX8 << addr << ")=";
uInt32 data;
switch(addr&0xF0000000)
{
case 0x00000000: //ROM
case 0x40000000: //RAM
data =read16(addr+2);
data<<=16;
data|=read16(addr+0);
if(DBUG)
statusMsg << HEX8 << data << endl;
return(data);
}
return fatalError("read32", addr, "abort");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Thumbulator::read_register ( uInt32 reg )
{
uInt32 data;
reg&=0xF;
if(DBUG)
statusMsg << "read_register(" << dec << reg << ")=";
switch(cpsr&0x1F)
{
case MODE_SVC:
switch(reg) // TODO (SA) - does this do anything other than default?
{
default: data=reg_sys[reg]; break;
case 13: case 14: data=reg_svc[reg]; break;
}
if(DBUG)
statusMsg << HEX8 << data << endl;
return(data);
}
return fatalError("read_register", cpsr, "invalid cpsr mode");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Thumbulator::write_register ( uInt32 reg, uInt32 data )
{
reg&=0xF;
if(DBUG)
statusMsg << "write_register(" << dec << reg << ","
<< HEX8 << data << ")" << endl;
switch(cpsr&0x1F)
{
case MODE_SVC:
switch(reg) // TODO (SA) - does this do anything other than default?
{
default: reg_sys[reg]=data; break;
case 13: case 14: reg_svc[reg]=data; break;
}
return(data);
}
return fatalError("write_register", cpsr, "invalid cpsr mode");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::do_zflag ( uInt32 x )
{
if(x==0) cpsr|=CPSR_Z;
else cpsr&=~CPSR_Z;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::do_nflag ( uInt32 x )
{
if(x&0x80000000) cpsr|=CPSR_N;
else cpsr&=~CPSR_N;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::do_cflag ( uInt32 a, uInt32 b, uInt32 c )
{
uInt32 rc;
cpsr&=~CPSR_C;
rc=(a&0x7FFFFFFF)+(b&0x7FFFFFFF)+c; //carry in
rc = (rc>>31)+(a>>31)+(b>>31); //carry out
if(rc&2)
cpsr|=CPSR_C;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::do_sub_vflag ( uInt32 a, uInt32 b, uInt32 c )
{
cpsr&=~CPSR_V;
//if the sign bits are different
if((a&0x80000000)^(b&0x80000000))
{
//and result matches b
if((b&0x80000000)==(c&0x80000000))
cpsr|=CPSR_V;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::do_add_vflag ( uInt32 a, uInt32 b, uInt32 c )
{
cpsr&=~CPSR_V;
//if sign bits are the same
if((a&0x80000000)==(b&0x80000000))
{
//and the result is different
if((b&0x80000000)!=(c&0x80000000))
cpsr|=CPSR_V;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::do_cflag_bit ( uInt32 x )
{
if(x) cpsr|=CPSR_C;
else cpsr&=~CPSR_C;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::do_vflag_bit ( uInt32 x )
{
if(x) cpsr|=CPSR_V;
else cpsr&=~CPSR_V;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Thumbulator::execute ( void )
{
uInt32 pc, sp, inst,
ra,rb,rc,
rm,rd,rn,rs,
op;
pc=read_register(15);
inst=fetch16(pc-2);
pc+=2;
write_register(15,pc);
if(DISS)
statusMsg << HEX8 << (pc-5) << ": " << HEX4 << inst << " ";
instructions++;
//ADC
if((inst&0xFFC0)==0x4140)
{
rd=(inst>>0)&0x07;
rm=(inst>>3)&0x07;
if(DISS)
statusMsg << "adc r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rd);
rb=read_register(rm);
rc=ra+rb;
if(cpsr&CPSR_C)
rc++;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
if(cpsr&CPSR_C) do_cflag(ra,rb,1);
else do_cflag(ra,rb,0);
do_add_vflag(ra,rb,rc);
return(0);
}
//ADD(1) small immediate two registers
if((inst&0xFE00)==0x1C00)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rb=(inst>>6)&0x7;
if(rb)
{
if(DISS)
statusMsg << "adds r" << dec << rd << ",r" << dec << rn << ","
<< "#0x" << HEX2 << rb << endl;
ra=read_register(rn);
rc=ra+rb;
//fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb);
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,rb,0);
do_add_vflag(ra,rb,rc);
return(0);
}
else
{
//this is a mov
}
}
//ADD(2) big immediate one register
if((inst&0xF800)==0x3000)
{
rb=(inst>>0)&0xFF;
rd=(inst>>8)&0x7;
if(DISS)
statusMsg << "adds r" << dec << rd << ",#0x" << HEX2 << rb << endl;
ra=read_register(rd);
rc=ra+rb;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,rb,0);
do_add_vflag(ra,-rb,rc);
return(0);
}
//ADD(3) three registers
if((inst&0xFE00)==0x1800)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rm=(inst>>6)&0x7;
if(DISS)
statusMsg << "adds r" << dec << rd << ",r" << dec << rn
<< ",r" << rm << endl;
ra=read_register(rn);
rb=read_register(rm);
rc=ra+rb;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,rb,0);
do_add_vflag(ra,rb,rc);
return(0);
}
//ADD(4) two registers one or both high no flags
if((inst&0xFF00)==0x4400)
{
if((inst>>6)&3)
{
//UNPREDICTABLE
}
rd=(inst>>0)&0x7;
rd|=(inst>>4)&0x8;
rm=(inst>>3)&0xF;
if(DISS)
statusMsg << "add r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rd);
rb=read_register(rm);
rc=ra+rb;
//fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb);
write_register(rd,rc);
return(0);
}
//ADD(5) rd = pc plus immediate
if((inst&0xF800)==0xA000)
{
rb=(inst>>0)&0xFF;
rd=(inst>>8)&0x7;
rb<<=2;
if(DISS)
statusMsg << "add r" << dec << rd << ",PC,#0x" << HEX2 << rb << endl;
ra=read_register(15);
rc=(ra&(~3))+rb;
write_register(rd,rc);
return(0);
}
//ADD(6) rd = sp plus immediate
if((inst&0xF800)==0xA800)
{
rb=(inst>>0)&0xFF;
rd=(inst>>8)&0x7;
rb<<=2;
if(DISS)
statusMsg << "add r" << dec << rd << ",SP,#0x" << HEX2 << rb << endl;
ra=read_register(13);
rc=ra+rb;
write_register(rd,rc);
return(0);
}
//ADD(7) sp plus immediate
if((inst&0xFF80)==0xB000)
{
rb=(inst>>0)&0x7F;
rb<<=2;
if(DISS)
statusMsg << "add SP,#0x" << HEX2 << rb << endl;
ra=read_register(13);
rc=ra+rb;
write_register(13,rc);
return(0);
}
//AND
if((inst&0xFFC0)==0x4000)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "ands r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rd);
rb=read_register(rm);
rc=ra&rb;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//ASR(1) two register immediate
if((inst&0xF800)==0x1000)
{
rd=(inst>>0)&0x07;
rm=(inst>>3)&0x07;
rb=(inst>>6)&0x1F;
if(DISS)
statusMsg << "asrs r" << dec << rd << ",r" << dec << rm
<< ",#0x" << HEX2 << rb << endl;
rc=read_register(rm);
if(rb==0)
{
if(rc&0x80000000)
{
do_cflag_bit(1);
rc=~0;
}
else
{
do_cflag_bit(0);
rc=0;
}
}
else
{
do_cflag_bit(rc&(1<<(rb-1)));
ra=rc&0x80000000;
rc>>=rb;
if(ra) //asr, sign is shifted in
{
rc|=(~0)<<(32-rb);
}
}
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//ASR(2) two register
if((inst&0xFFC0)==0x4100)
{
rd=(inst>>0)&0x07;
rs=(inst>>3)&0x07;
if(DISS)
statusMsg << "asrs r" << dec << rd << ",r" << dec << rs << endl;
rc=read_register(rd);
rb=read_register(rs);
rb&=0xFF;
if(rb==0)
{
}
else if(rb<32)
{
do_cflag_bit(rc&(1<<(rb-1)));
ra=rc&0x80000000;
rc>>=rb;
if(ra) //asr, sign is shifted in
{
rc|=(~0)<<(32-rb);
}
}
else
{
if(rc&0x80000000)
{
do_cflag_bit(1);
rc=(~0);
}
else
{
do_cflag_bit(0);
rc=0;
}
}
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//B(1) conditional branch
if((inst&0xF000)==0xD000)
{
rb=(inst>>0)&0xFF;
if(rb&0x80)
rb|=(~0)<<8;
op=(inst>>8)&0xF;
rb<<=1;
rb+=pc;
rb+=2;
switch(op)
{
case 0x0: //b eq z set
if(DISS)
statusMsg << "beq 0x" << HEX8 << (rb-3) << endl;
if(cpsr&CPSR_Z)
{
write_register(15,rb);
}
return(0);
case 0x1: //b ne z clear
if(DISS)
statusMsg << "bne 0x" << HEX8 << (rb-3) << endl;
if(!(cpsr&CPSR_Z))
{
write_register(15,rb);
}
return(0);
case 0x2: //b cs c set
if(DISS)
statusMsg << "bcs 0x" << HEX8 << (rb-3) << endl;
if(cpsr&CPSR_C)
{
write_register(15,rb);
}
return(0);
case 0x3: //b cc c clear
if(DISS)
statusMsg << "bcc 0x" << HEX8 << (rb-3) << endl;
if(!(cpsr&CPSR_C))
{
write_register(15,rb);
}
return(0);
case 0x4: //b mi n set
if(DISS)
statusMsg << "bmi 0x" << HEX8 << (rb-3) << endl;
if(cpsr&CPSR_N)
{
write_register(15,rb);
}
return(0);
case 0x5: //b pl n clear
if(DISS)
statusMsg << "bpl 0x" << HEX8 << (rb-3) << endl;
if(!(cpsr&CPSR_N))
{
write_register(15,rb);
}
return(0);
case 0x6: //b vs v set
if(DISS)
statusMsg << "bvs 0x" << HEX8 << (rb-3) << endl;
if(cpsr&CPSR_V)
{
write_register(15,rb);
}
return(0);
case 0x7: //b vc v clear
if(DISS)
statusMsg << "bvc 0x" << HEX8 << (rb-3) << endl;
if(!(cpsr&CPSR_V))
{
write_register(15,rb);
}
return(0);
case 0x8: //b hi c set z clear
if(DISS)
statusMsg << "bhi 0x" << HEX8 << (rb-3) << endl;
if((cpsr&CPSR_C)&&(!(cpsr&CPSR_Z)))
{
write_register(15,rb);
}
return(0);
case 0x9: //b ls c clear or z set
if(DISS)
statusMsg << "bls 0x" << HEX8 << (rb-3) << endl;
if((cpsr&CPSR_Z)||(!(cpsr&CPSR_C)))
{
write_register(15,rb);
}
return(0);
case 0xA: //b ge N == V
if(DISS)
statusMsg << "bge 0x" << HEX8 << (rb-3) << endl;
ra=0;
if( (cpsr&CPSR_N) && (cpsr&CPSR_V) ) ra++;
if((!(cpsr&CPSR_N))&&(!(cpsr&CPSR_V))) ra++;
if(ra)
{
write_register(15,rb);
}
return(0);
case 0xB: //b lt N != V
if(DISS)
statusMsg << "blt 0x" << HEX8 << (rb-3) << endl;
ra=0;
if((!(cpsr&CPSR_N))&&(cpsr&CPSR_V)) ra++;
if((!(cpsr&CPSR_V))&&(cpsr&CPSR_N)) ra++;
if(ra)
{
write_register(15,rb);
}
return(0);
case 0xC: //b gt Z==0 and N == V
if(DISS)
statusMsg << "bgt 0x" << HEX8 << (rb-3) << endl;
ra=0;
if( (cpsr&CPSR_N) && (cpsr&CPSR_V) ) ra++;
if((!(cpsr&CPSR_N))&&(!(cpsr&CPSR_V))) ra++;
if(cpsr&CPSR_Z) ra=0;
if(ra)
{
write_register(15,rb);
}
return(0);
case 0xD: //b le Z==1 or N != V
if(DISS)
statusMsg << "ble 0x" << HEX8 << (rb-3) << endl;
ra=0;
if((!(cpsr&CPSR_N))&&(cpsr&CPSR_V)) ra++;
if((!(cpsr&CPSR_V))&&(cpsr&CPSR_N)) ra++;
if(cpsr&CPSR_Z) ra++;
if(ra)
{
write_register(15,rb);
}
return(0);
case 0xE:
//undefined instruction
break;
case 0xF:
//swi
break;
}
}
//B(2) unconditional branch
if((inst&0xF800)==0xE000)
{
rb=(inst>>0)&0x7FF;
if(rb&(1<<10))
rb|=(~0)<<11;
rb<<=1;
rb+=pc;
rb+=2;
if(DISS)
statusMsg << "B 0x" << HEX8 << (rb-3) << endl;
write_register(15,rb);
return(0);
}
//BIC
if((inst&0xFFC0)==0x4380)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "bics r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rd);
rb=read_register(rm);
rc=ra&(~rb);
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//BKPT
if((inst&0xFF00)==0xBE00)
{
rb=(inst>>0)&0xFF;
statusMsg << "bkpt 0x" << HEX2 << rb << endl;
return(1);
}
//BL/BLX(1)
if((inst&0xE000)==0xE000) //BL,BLX
{
if((inst&0x1800)==0x1000) //H=b10
{
if(DISS)
statusMsg << endl;
halfadd=inst;
return(0);
}
else if((inst&0x1800)==0x1800) //H=b11
{
//branch to thumb
rb=halfadd&((1<<11)-1);
if(rb&1<<10)
rb|=(~((1<<11)-1)); //sign extend
rb<<=11;
rb|=inst&((1<<11)-1);
rb<<=1;
rb+=pc;
if(DISS)
statusMsg << "bl 0x" << HEX8 << (rb-3) << endl;
write_register(14,pc-2);
write_register(15,rb);
return(0);
}
else if((inst&0x1800)==0x0800) //H=b01
{
//fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst);
// fxq: this should exit the code without having to detect it
return(1);
}
}
//BLX(2)
if((inst&0xFF87)==0x4780)
{
rm=(inst>>3)&0xF;
if(DISS)
statusMsg << "blx r" << dec << rm << endl;
rc=read_register(rm);
//fprintf(stderr,"blx r%u 0x%X 0x%X\n",rm,rc,pc);
rc+=2;
if(rc&1)
{
write_register(14,pc-2);
write_register(15,rc);
return(0);
}
else
{
//fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst);
// fxq: this could serve as exit code
return(1);
}
}
//BX
if((inst&0xFF87)==0x4700)
{
rm=(inst>>3)&0xF;
if(DISS)
statusMsg << "bx r" << dec << rm << endl;
rc=read_register(rm);
rc+=2;
//fprintf(stderr,"bx r%u 0x%X 0x%X\n",rm,rc,pc);
if(rc&1)
{
write_register(15,rc);
return(0);
}
else
{
//fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst);
// fxq: or maybe this one??
return(1);
}
}
//CMN
if((inst&0xFFC0)==0x42C0)
{
rn=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "cmns r" << dec << rn << ",r" << dec << rm << endl;
ra=read_register(rn);
rb=read_register(rm);
rc=ra+rb;
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,rb,0);
do_add_vflag(ra,rb,rc);
return(0);
}
//CMP(1) compare immediate
if((inst&0xF800)==0x2800)
{
rb=(inst>>0)&0xFF;
rn=(inst>>8)&0x07;
if(DISS)
statusMsg << "cmp r" << dec << rn << ",#0x" << HEX2 << rb << endl;
ra=read_register(rn);
rc=ra-rb;
//fprintf(stderr,"0x%08X 0x%08X\n",ra,rb);
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,~rb,1);
do_sub_vflag(ra,rb,rc);
return(0);
}
//CMP(2) compare register
if((inst&0xFFC0)==0x4280)
{
rn=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "cmps r" << dec << rn << ",r" << dec << rm << endl;
ra=read_register(rn);
rb=read_register(rm);
rc=ra-rb;
//fprintf(stderr,"0x%08X 0x%08X\n",ra,rb);
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,~rb,1);
do_sub_vflag(ra,rb,rc);
return(0);
}
//CMP(3) compare high register
if((inst&0xFF00)==0x4500)
{
if(((inst>>6)&3)==0x0)
{
//UNPREDICTABLE
}
rn=(inst>>0)&0x7;
rn|=(inst>>4)&0x8;
if(rn==0xF)
{
//UNPREDICTABLE
}
rm=(inst>>3)&0xF;
if(DISS)
statusMsg << "cmps r" << dec << rn << ",r" << dec << rm << endl;
ra=read_register(rn);
rb=read_register(rm);
rc=ra-rb;
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,~rb,1);
do_sub_vflag(ra,rb,rc);
#if 0
if(cpsr&CPSR_N) statusMsg << "N"; else statusMsg << "n";
if(cpsr&CPSR_Z) statusMsg << "Z"; else statusMsg << "z";
if(cpsr&CPSR_C) statusMsg << "C"; else statusMsg << "c";
if(cpsr&CPSR_V) statusMsg << "V"; else statusMsg << "v";
statusMsg << " -- 0x" << HEX8 << ra << " 0x" << HEX8 << rb << endl;
#endif
return(0);
}
//CPS
if((inst&0xFFE8)==0xB660)
{
if(DISS)
statusMsg << "cps TODO" << endl;
return(1);
}
//CPY copy high register
if((inst&0xFFC0)==0x4600)
{
//same as mov except you can use both low registers
//going to let mov handle high registers
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "cpy r" << dec << rd << ",r" << dec << rm << endl;
rc=read_register(rm);
write_register(rd,rc);
return(0);
}
//EOR
if((inst&0xFFC0)==0x4040)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "eors r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rd);
rb=read_register(rm);
rc=ra^rb;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//LDMIA
if((inst&0xF800)==0xC800)
{
rn=(inst>>8)&0x7;
if(DISS)
{
statusMsg << "ldmia r" << dec << rn << "!,{";
for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,ra++)
{
if(inst&rb)
{
if(rc) statusMsg << ",";
statusMsg << "r" << dec << ra;
rc++;
}
}
statusMsg << "}" << endl;
}
sp=read_register(rn);
for(ra=0,rb=0x01;rb;rb=(rb<<1)&0xFF,ra++)
{
if(inst&rb)
{
write_register(ra,read32(sp));
sp+=4;
}
}
write_register(rn,sp);
return(0);
}
//LDR(1) two register immediate
if((inst&0xF800)==0x6800)
{
rd=(inst>>0)&0x07;
rn=(inst>>3)&0x07;
rb=(inst>>6)&0x1F;
rb<<=2;
if(DISS)
statusMsg << "ldr r" << dec << rd << ",[r" << dec << rn
<< ",#0x" << HEX2 << rb << "]" << endl;
rb=read_register(rn)+rb;
rc=read32(rb);
write_register(rd,rc);
return(0);
}
//LDR(2) three register
if((inst&0xFE00)==0x5800)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rm=(inst>>6)&0x7;
if(DISS)
statusMsg << "ldr r" << dec << rd << ",[r" << dec << rn
<< ",r" << dec << "]" << endl;
rb=read_register(rn)+read_register(rm);
rc=read32(rb);
write_register(rd,rc);
return(0);
}
//LDR(3)
if((inst&0xF800)==0x4800)
{
rb=(inst>>0)&0xFF;
rd=(inst>>8)&0x07;
rb<<=2;
if(DISS)
statusMsg << "ldr r" << dec << rd << ",[PC+#0x" << HEX2 << rb << "] ";
ra=read_register(15);
ra&=~3;
rb+=ra;
if(DISS)
statusMsg << ";@ 0x" << HEX2 << rb << endl;
rc=read32(rb);
write_register(rd,rc);
return(0);
}
//LDR(4)
if((inst&0xF800)==0x9800)
{
rb=(inst>>0)&0xFF;
rd=(inst>>8)&0x07;
rb<<=2;
if(DISS)
statusMsg << "ldr r" << dec << rd << ",[SP+#0x" << HEX2 << rb
<< "]" << endl;
ra=read_register(13);
//ra&=~3;
rb+=ra;
rc=read32(rb);
write_register(rd,rc);
return(0);
}
//LDRB(1)
if((inst&0xF800)==0x7800)
{
rd=(inst>>0)&0x07;
rn=(inst>>3)&0x07;
rb=(inst>>6)&0x1F;
if(DISS)
statusMsg << "ldrb r" << dec << rd << ",[r" << dec << rn
<< ",#0x" << HEX2 << rb << "]" << endl;
rb=read_register(rn)+rb;
rc=read16(rb&(~1));
if(rb&1)
{
rc>>=8;
}
else
{
}
write_register(rd,rc&0xFF);
return(0);
}
//LDRB(2)
if((inst&0xFE00)==0x5C00)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rm=(inst>>6)&0x7;
if(DISS)
statusMsg << "ldrb r" << dec << rd << ",[r" << dec << rn
<< ",r" << dec << rm << "]" << endl;
rb=read_register(rn)+read_register(rm);
rc=read16(rb&(~1));
if(rb&1)
{
rc>>=8;
}
else
{
}
write_register(rd,rc&0xFF);
return(0);
}
//LDRH(1)
if((inst&0xF800)==0x8800)
{
rd=(inst>>0)&0x07;
rn=(inst>>3)&0x07;
rb=(inst>>6)&0x1F;
rb<<=1;
if(DISS)
statusMsg << "ldrh r" << dec << rd << ",[r" << dec << rn
<< ",#0x" << HEX2 << rb << "]" << endl;
rb=read_register(rn)+rb;
rc=read16(rb);
write_register(rd,rc&0xFFFF);
return(0);
}
//LDRH(2)
if((inst&0xFE00)==0x5A00)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rm=(inst>>6)&0x7;
if(DISS)
statusMsg << "ldrh r" << dec << rd << ",[r" << dec << rn
<< ",r" << dec << rm << "]" << endl;
rb=read_register(rn)+read_register(rm);
rc=read16(rb);
write_register(rd,rc&0xFFFF);
return(0);
}
//LDRSB
if((inst&0xFE00)==0x5600)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rm=(inst>>6)&0x7;
if(DISS)
statusMsg << "ldrsb r" << dec << rd << ",[r" << dec << rn
<< ",r" << dec << rm << "]" << endl;
rb=read_register(rn)+read_register(rm);
rc=read16(rb&(~1));
if(rb&1)
{
rc>>=8;
}
else
{
}
rc&=0xFF;
if(rc&0x80) rc|=((~0)<<8);
write_register(rd,rc);
return(0);
}
//LDRSH
if((inst&0xFE00)==0x5E00)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rm=(inst>>6)&0x7;
if(DISS)
statusMsg << "ldrsh r" << dec << rd << ",[r" << dec << rn
<< ",r" << dec << rm << "]" << endl;
rb=read_register(rn)+read_register(rm);
rc=read16(rb);
rc&=0xFFFF;
if(rc&0x8000) rc|=((~0)<<16);
write_register(rd,rc);
return(0);
}
//LSL(1)
if((inst&0xF800)==0x0000)
{
rd=(inst>>0)&0x07;
rm=(inst>>3)&0x07;
rb=(inst>>6)&0x1F;
if(DISS)
statusMsg << "lsls r" << dec << rd << ",r" << dec << rm
<< ",#0x" << HEX2 << rb << endl;
rc=read_register(rm);
if(rb==0)
{
//if immed_5 == 0
//C unnaffected
//result not shifted
}
else
{
//else immed_5 > 0
do_cflag_bit(rc&(1<<(32-rb)));
rc<<=rb;
}
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//LSL(2) two register
if((inst&0xFFC0)==0x4080)
{
rd=(inst>>0)&0x07;
rs=(inst>>3)&0x07;
if(DISS)
statusMsg << "lsls r" << dec << rd << ",r" << dec << rs << endl;
rc=read_register(rd);
rb=read_register(rs);
rb&=0xFF;
if(rb==0)
{
}
else if(rb<32)
{
do_cflag_bit(rc&(1<<(32-rb)));
rc<<=rb;
}
else if(rb==32)
{
do_cflag_bit(rc&1);
rc=0;
}
else
{
do_cflag_bit(0);
rc=0;
}
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//LSR(1) two register immediate
if((inst&0xF800)==0x0800)
{
rd=(inst>>0)&0x07;
rm=(inst>>3)&0x07;
rb=(inst>>6)&0x1F;
if(DISS)
statusMsg << "lsrs r" << dec << rd << ",r" << dec << rm
<< ",#0x" << HEX2 << rb << endl;
rc=read_register(rm);
if(rb==0)
{
do_cflag_bit(rc&0x80000000);
rc=0;
}
else
{
do_cflag_bit(rc&(1<<(rb-1)));
rc>>=rb;
}
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//LSR(2) two register
if((inst&0xFFC0)==0x40C0)
{
rd=(inst>>0)&0x07;
rs=(inst>>3)&0x07;
if(DISS)
statusMsg << "lsrs r" << dec << rd << ",r" << dec << rs << endl;
rc=read_register(rd);
rb=read_register(rs);
rb&=0xFF;
if(rb==0)
{
}
else if(rb<32)
{
do_cflag_bit(rc&(1<<(32-rb)));
rc>>=rb;
}
else if(rb==32)
{
do_cflag_bit(rc&0x80000000);
rc=0;
}
else
{
do_cflag_bit(0);
rc=0;
}
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//MOV(1) immediate
if((inst&0xF800)==0x2000)
{
rb=(inst>>0)&0xFF;
rd=(inst>>8)&0x07;
if(DISS)
statusMsg << "movs r" << dec << rd << ",#0x" << HEX2 << rb << endl;
write_register(rd,rb);
do_nflag(rb);
do_zflag(rb);
return(0);
}
//MOV(2) two low registers
if((inst&0xFFC0)==0x1C00)
{
rd=(inst>>0)&7;
rn=(inst>>3)&7;
if(DISS)
statusMsg << "movs r" << dec << rd << ",r" << dec << rn << endl;
rc=read_register(rn);
//fprintf(stderr,"0x%08X\n",rc);
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
do_cflag_bit(0);
do_vflag_bit(0);
return(0);
}
//MOV(3)
if((inst&0xFF00)==0x4600)
{
rd=(inst>>0)&0x7;
rd|=(inst>>4)&0x8;
rm=(inst>>3)&0xF;
if(DISS)
statusMsg << "mov r" << dec << rd << ",r" << dec << rm << endl;
rc=read_register(rm);
if (rd==15) rc+=2; // fxq fix for MOV R15
write_register(rd,rc);
return(0);
}
//MUL
if((inst&0xFFC0)==0x4340)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "muls r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rd);
rb=read_register(rm);
rc=ra*rb;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//MVN
if((inst&0xFFC0)==0x43C0)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "mvns r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rm);
rc=(~ra);
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//NEG
if((inst&0xFFC0)==0x4240)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "negs r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rm);
rc=0-ra;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
do_cflag(0,~ra,1);
do_sub_vflag(0,ra,rc);
return(0);
}
//ORR
if((inst&0xFFC0)==0x4300)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "orrs r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rd);
rb=read_register(rm);
rc=ra|rb;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//POP
if((inst&0xFE00)==0xBC00)
{
if(DISS)
{
statusMsg << "pop {";
for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,ra++)
{
if(inst&rb)
{
if(rc) statusMsg << ",";
statusMsg << "r" << dec << ra;
rc++;
}
}
if(inst&0x100)
{
if(rc) statusMsg << ",";
statusMsg << "pc";
}
statusMsg << "}" << endl;
}
sp=read_register(13);
for(ra=0,rb=0x01;rb;rb=(rb<<1)&0xFF,ra++)
{
if(inst&rb)
{
write_register(ra,read32(sp));
sp+=4;
}
}
if(inst&0x100)
{
rc=read32(sp);
rc+=2;
write_register(15,rc);
sp+=4;
}
write_register(13,sp);
return(0);
}
//PUSH
if((inst&0xFE00)==0xB400)
{
if(DISS)
{
statusMsg << "push {";
for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,ra++)
{
if(inst&rb)
{
if(rc) statusMsg << ",";
statusMsg << "r" << dec << ra;
rc++;
}
}
if(inst&0x100)
{
if(rc) statusMsg << ",";
statusMsg << "lr";
}
statusMsg << "}" << endl;
}
sp=read_register(13);
//fprintf(stderr,"sp 0x%08X\n",sp);
for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,ra++)
{
if(inst&rb)
{
rc++;
}
}
if(inst&0x100) rc++;
rc<<=2;
sp-=rc;
rd=sp;
for(ra=0,rb=0x01;rb;rb=(rb<<1)&0xFF,ra++)
{
if(inst&rb)
{
write32(rd,read_register(ra));
rd+=4;
}
}
if(inst&0x100)
{
write32(rd,read_register(14));
}
write_register(13,sp);
return(0);
}
//REV
if((inst&0xFFC0)==0xBA00)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
if(DISS)
statusMsg << "rev r" << dec << rd << ",r" << dec << rn << endl;
ra=read_register(rn);
rc =((ra>> 0)&0xFF)<<24;
rc|=((ra>> 8)&0xFF)<<16;
rc|=((ra>>16)&0xFF)<< 8;
rc|=((ra>>24)&0xFF)<< 0;
write_register(rd,rc);
return(0);
}
//REV16
if((inst&0xFFC0)==0xBA40)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
if(DISS)
statusMsg << "rev16 r" << dec << rd << ",r" << dec << rn << endl;
ra=read_register(rn);
rc =((ra>> 0)&0xFF)<< 8;
rc|=((ra>> 8)&0xFF)<< 0;
rc|=((ra>>16)&0xFF)<<24;
rc|=((ra>>24)&0xFF)<<16;
write_register(rd,rc);
return(0);
}
//REVSH
if((inst&0xFFC0)==0xBAC0)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
if(DISS)
statusMsg << "revsh r" << dec << rd << ",r" << dec << rn << endl;
ra=read_register(rn);
rc =((ra>> 0)&0xFF)<< 8;
rc|=((ra>> 8)&0xFF)<< 0;
if(rc&0x8000) rc|=0xFFFF0000;
else rc&=0x0000FFFF;
write_register(rd,rc);
return(0);
}
//ROR
if((inst&0xFFC0)==0x41C0)
{
rd=(inst>>0)&0x7;
rs=(inst>>3)&0x7;
if(DISS)
statusMsg << "rors r" << dec << rd << ",r" << dec << rs << endl;
rc=read_register(rd);
ra=read_register(rs);
ra&=0xFF;
if(ra==0)
{
}
else
{
ra&=0x1F;
if(ra==0)
{
do_cflag_bit(rc&0x80000000);
}
else
{
do_cflag_bit(rc&(1<<(ra-1)));
rb=rc<<(32-ra);
rc>>=ra;
rc|=rb;
}
}
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
return(0);
}
//SBC
if((inst&0xFFC0)==0x4180)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "sbc r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rd);
rb=read_register(rm);
rc=ra-rb;
if(!(cpsr&CPSR_C)) rc--;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,rb,0);
do_sub_vflag(ra,rb,rc);
return(0);
}
//SETEND
if((inst&0xFFF7)==0xB650)
{
statusMsg << "setend not implemented" << endl;
return(1);
}
//STMIA
if((inst&0xF800)==0xC000)
{
rn=(inst>>8)&0x7;
if(DISS)
{
statusMsg << "stmia r" << dec << rn << "!,{";
for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,ra++)
{
if(inst&rb)
{
if(rc) statusMsg << ",";
statusMsg << "r" << dec << ra;
rc++;
}
}
statusMsg << "}" << endl;
}
sp=read_register(rn);
for(ra=0,rb=0x01;rb;rb=(rb<<1)&0xFF,ra++)
{
if(inst&rb)
{
write32(sp,read_register(ra));
sp+=4;
}
}
write_register(rn,sp);
return(0);
}
//STR(1)
if((inst&0xF800)==0x6000)
{
rd=(inst>>0)&0x07;
rn=(inst>>3)&0x07;
rb=(inst>>6)&0x1F;
rb<<=2;
if(DISS)
statusMsg << "str r" << dec << rd << ",[r" << dec << rn
<< ",#0x" << HEX2 << rb << "]" << endl;
rb=read_register(rn)+rb;
rc=read_register(rd);
write32(rb,rc);
return(0);
}
//STR(2)
if((inst&0xFE00)==0x5000)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rm=(inst>>6)&0x7;
if(DISS)
statusMsg << "str r" << dec << rd << ",[r" << dec << rn
<< ",r" << dec << rm << "]" << endl;
rb=read_register(rn)+read_register(rm);
rc=read_register(rd);
write32(rb,rc);
return(0);
}
//STR(3)
if((inst&0xF800)==0x9000)
{
rb=(inst>>0)&0xFF;
rd=(inst>>8)&0x07;
rb<<=2;
if(DISS)
statusMsg << "str r" << dec << rd << ",[SP,#0x" << HEX2 << rb << "]" << endl;
rb=read_register(13)+rb;
//fprintf(stderr,"0x%08X\n",rb);
rc=read_register(rd);
write32(rb,rc);
return(0);
}
//STRB(1)
if((inst&0xF800)==0x7000)
{
rd=(inst>>0)&0x07;
rn=(inst>>3)&0x07;
rb=(inst>>6)&0x1F;
if(DISS)
statusMsg << "strb r" << dec << rd << ",[r" << dec << rn
<< ",#0x" << HEX8 << rb << "]" << endl;
rb=read_register(rn)+rb;
rc=read_register(rd);
ra=read16(rb&(~1));
if(rb&1)
{
ra&=0x00FF;
ra|=rc<<8;
}
else
{
ra&=0xFF00;
ra|=rc&0x00FF;
}
write16(rb&(~1),ra&0xFFFF);
return(0);
}
//STRB(2)
if((inst&0xFE00)==0x5400)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rm=(inst>>6)&0x7;
if(DISS)
statusMsg << "strb r" << dec << rd << ",[r" << dec << rn
<< ",r" << rm << "]" << endl;
rb=read_register(rn)+read_register(rm);
rc=read_register(rd);
ra=read16(rb&(~1));
if(rb&1)
{
ra&=0x00FF;
ra|=rc<<8;
}
else
{
ra&=0xFF00;
ra|=rc&0x00FF;
}
write16(rb&(~1),ra&0xFFFF);
return(0);
}
//STRH(1)
if((inst&0xF800)==0x8000)
{
rd=(inst>>0)&0x07;
rn=(inst>>3)&0x07;
rb=(inst>>6)&0x1F;
rb<<=1;
if(DISS)
statusMsg << "strh r" << dec << rd << ",[r" << dec << rn
<< ",#0x" << HEX2 << rb << "]" << endl;
rb=read_register(rn)+rb;
rc=read_register(rd);
write16(rb,rc&0xFFFF);
return(0);
}
//STRH(2)
if((inst&0xFE00)==0x5200)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rm=(inst>>6)&0x7;
if(DISS)
statusMsg << "strh r" << dec << rd << ",[r" << dec << rn
<< ",r" << dec << rm << "]" << endl;
rb=read_register(rn)+read_register(rm);
rc=read_register(rd);
write16(rb,rc&0xFFFF);
return(0);
}
//SUB(1)
if((inst&0xFE00)==0x1E00)
{
rd=(inst>>0)&7;
rn=(inst>>3)&7;
rb=(inst>>6)&7;
if(DISS)
statusMsg << "subs r" << dec << rd << ",r" << dec << rn
<< ",#0x" << HEX2 << rb << endl;
ra=read_register(rn);
rc=ra-rb;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,~rb,1);
do_sub_vflag(ra,rb,rc);
return(0);
}
//SUB(2)
if((inst&0xF800)==0x3800)
{
rb=(inst>>0)&0xFF;
rd=(inst>>8)&0x07;
if(DISS)
statusMsg << "subs r" << dec << rd << ",#0x" << HEX2 << rb << endl;
ra=read_register(rd);
rc=ra-rb;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,~rb,1);
do_sub_vflag(ra,rb,rc);
return(0);
}
//SUB(3)
if((inst&0xFE00)==0x1A00)
{
rd=(inst>>0)&0x7;
rn=(inst>>3)&0x7;
rm=(inst>>6)&0x7;
if(DISS)
statusMsg << "subs r" << dec << rd << ",r" << dec << rn
<< ",r" << dec << rm << endl;
ra=read_register(rn);
rb=read_register(rm);
rc=ra-rb;
write_register(rd,rc);
do_nflag(rc);
do_zflag(rc);
do_cflag(ra,~rb,1);
do_sub_vflag(ra,rb,rc);
return(0);
}
//SUB(4)
if((inst&0xFF80)==0xB080)
{
rb=inst&0x7F;
rb<<=2;
if(DISS)
statusMsg << "sub SP,#0x" << HEX2 << rb << endl;
ra=read_register(13);
ra-=rb;
write_register(13,ra);
return(0);
}
//SWI
if((inst&0xFF00)==0xDF00)
{
rb=inst&0xFF;
if(DISS)
statusMsg << "swi 0x" << HEX2 << rb << endl;
statusMsg << endl << endl << "swi 0x" << HEX2 << rb << endl;
return(1);
}
//SXTB
if((inst&0xFFC0)==0xB240)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "sxtb r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rm);
rc=ra&0xFF;
if(rc&0x80) rc|=(~0)<<8;
write_register(rd,rc);
return(0);
}
//SXTH
if((inst&0xFFC0)==0xB200)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "sxth r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rm);
rc=ra&0xFFFF;
if(rc&0x8000) rc|=(~0)<<16;
write_register(rd,rc);
return(0);
}
//TST
if((inst&0xFFC0)==0x4200)
{
rn=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "tst r" << dec << rn << ",r" << dec << rm << endl;
ra=read_register(rn);
rb=read_register(rm);
rc=ra&rb;
do_nflag(rc);
do_zflag(rc);
return(0);
}
//UXTB
if((inst&0xFFC0)==0xB2C0)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "uxtb r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rm);
rc=ra&0xFF;
write_register(rd,rc);
return(0);
}
//UXTH
if((inst&0xFFC0)==0xB280)
{
rd=(inst>>0)&0x7;
rm=(inst>>3)&0x7;
if(DISS)
statusMsg << "uxth r" << dec << rd << ",r" << dec << rm << endl;
ra=read_register(rm);
rc=ra&0xFFFF;
write_register(rd,rc);
return(0);
}
statusMsg << "invalid instruction " << HEX8 << pc << " " << HEX4 << inst << endl;
return(1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Thumbulator::reset ( void )
{
//memset(ram,0xFF,sizeof(ram));
cpsr=CPSR_T|CPSR_I|CPSR_F|MODE_SVC;
reg_svc[13]=0x40001fb4; //sp
reg_svc[14]=0x00000c00; //lr (duz this use odd addrs)
reg_sys[15]=0x00000c0b; // entry point of 0xc09+2
// reg_sys[15]+=2;
// fxq: don't care about below so much (maybe to guess timing???)
instructions=0;
fetches=0;
reads=0;
writes=0;
statusMsg.str("");
return(0);
}
#endif