diff options
author | Alexey Yakovenko <wakeroid@gmail.com> | 2010-06-22 22:26:45 +0200 |
---|---|---|
committer | Alexey Yakovenko <wakeroid@gmail.com> | 2010-06-22 22:26:45 +0200 |
commit | 20575a338e9640eca924958484a5fee800e09971 (patch) | |
tree | efaeac41a8a21bea1c90494b3b0968169d4f8732 /plugins/ao/eng_dsf | |
parent | c901a7cbf52ee234220f21b85c2e77667264d16c (diff) |
audio overload plugin - highly experimental; no seeking; crashes
Diffstat (limited to 'plugins/ao/eng_dsf')
-rw-r--r-- | plugins/ao/eng_dsf/aica.c | 1280 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/aica.h | 44 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/aicadsp.c | 349 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/aicadsp.h | 37 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/aicalfo.c | 159 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/arm7.c | 274 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/arm7.h | 165 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/arm7i.c | 1339 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/arm7i.h | 19 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/arm7memil.c | 56 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/arm7thumb.c | 1176 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/arm7thumb.h | 117 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/dc_hw.c | 174 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/dc_hw.h | 9 | ||||
-rw-r--r-- | plugins/ao/eng_dsf/eng_dsf.c | 256 |
15 files changed, 5454 insertions, 0 deletions
diff --git a/plugins/ao/eng_dsf/aica.c b/plugins/ao/eng_dsf/aica.c new file mode 100644 index 00000000..ad0cde3c --- /dev/null +++ b/plugins/ao/eng_dsf/aica.c @@ -0,0 +1,1280 @@ +/* + Sega/Yamaha AICA emulation + By ElSemi, kingshriek, and R. Belmont + + This is effectively a 64-voice SCSP, with the following differences: + - No FM mode + - A third sample format (ADPCM) has been added + - Some minor other tweeks +*/ + +#include <math.h> +#include <string.h> +#include "ao.h" +#include "cpuintrf.h" +#include "aica.h" +#include "aicadsp.h" +#include "dc_hw.h" + +#define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x) + +#define SHIFT 12 +#define FIX(v) ((UINT32) ((float) (1<<SHIFT)*(v))) + + +#define EG_SHIFT 16 + +#define USEDSP + +// include the LFO handling code +#include "aicalfo.c" + +/* + AICA features 64 programmable slots + that can generate PCM and ADPCM (from ROM/RAM) sound +*/ + +//SLOT PARAMETERS +#define KEYONEX(slot) ((slot->udata.data[0x0]>>0x0)&0x8000) +#define KEYONB(slot) ((slot->udata.data[0x0]>>0x0)&0x4000) +#define SSCTL(slot) ((slot->udata.data[0x0]>>0xA)&0x0001) +#define LPCTL(slot) ((slot->udata.data[0x0]>>0x9)&0x0001) +#define PCMS(slot) ((slot->udata.data[0x0]>>0x7)&0x0003) + +#define SA(slot) (((slot->udata.data[0x0]&0x7F)<<16)|(slot->udata.data[0x4/2])) + +#define LSA(slot) (slot->udata.data[0x8/2]) + +#define LEA(slot) (slot->udata.data[0xc/2]) + +#define D2R(slot) ((slot->udata.data[0x10/2]>>0xB)&0x001F) +#define D1R(slot) ((slot->udata.data[0x10/2]>>0x6)&0x001F) +#define AR(slot) ((slot->udata.data[0x10/2]>>0x0)&0x001F) + +#define LPSLNK(slot) ((slot->udata.data[0x14/2]>>0x0)&0x4000) +#define KRS(slot) ((slot->udata.data[0x14/2]>>0xA)&0x000F) +#define DL(slot) ((slot->udata.data[0x14/2]>>0x5)&0x001F) +#define RR(slot) ((slot->udata.data[0x14/2]>>0x0)&0x001F) + +#define TL(slot) ((slot->udata.data[0x28/2]>>0x8)&0x00FF) + +#define OCT(slot) ((slot->udata.data[0x18/2]>>0xB)&0x000F) +#define FNS(slot) ((slot->udata.data[0x18/2]>>0x0)&0x03FF) + +#define LFORE(slot) ((slot->udata.data[0x1c/2]>>0x0)&0x8000) +#define LFOF(slot) ((slot->udata.data[0x1c/2]>>0xA)&0x001F) +#define PLFOWS(slot) ((slot->udata.data[0x1c/2]>>0x8)&0x0003) +#define PLFOS(slot) ((slot->udata.data[0x1c/2]>>0x5)&0x0007) +#define ALFOWS(slot) ((slot->udata.data[0x1c/2]>>0x3)&0x0003) +#define ALFOS(slot) ((slot->udata.data[0x1c/2]>>0x0)&0x0007) + +#define ISEL(slot) ((slot->udata.data[0x20/2]>>0x0)&0x000F) +#define IMXL(slot) ((slot->udata.data[0x20/2]>>0x4)&0x000F) + +#define DISDL(slot) ((slot->udata.data[0x24/2]>>0x8)&0x000F) +#define DIPAN(slot) ((slot->udata.data[0x24/2]>>0x0)&0x001F) + +#define EFSDL(slot) ((AICA->EFSPAN[slot*4]>>8)&0x000f) +#define EFPAN(slot) ((AICA->EFSPAN[slot*4]>>0)&0x001f) + +//Envelope times in ms +static const double ARTimes[64]={100000/*infinity*/,100000/*infinity*/,8100.0,6900.0,6000.0,4800.0,4000.0,3400.0,3000.0,2400.0,2000.0,1700.0,1500.0, + 1200.0,1000.0,860.0,760.0,600.0,500.0,430.0,380.0,300.0,250.0,220.0,190.0,150.0,130.0,110.0,95.0, + 76.0,63.0,55.0,47.0,38.0,31.0,27.0,24.0,19.0,15.0,13.0,12.0,9.4,7.9,6.8,6.0,4.7,3.8,3.4,3.0,2.4, + 2.0,1.8,1.6,1.3,1.1,0.93,0.85,0.65,0.53,0.44,0.40,0.35,0.0,0.0}; +static const double DRTimes[64]={100000/*infinity*/,100000/*infinity*/,118200.0,101300.0,88600.0,70900.0,59100.0,50700.0,44300.0,35500.0,29600.0,25300.0,22200.0,17700.0, + 14800.0,12700.0,11100.0,8900.0,7400.0,6300.0,5500.0,4400.0,3700.0,3200.0,2800.0,2200.0,1800.0,1600.0,1400.0,1100.0, + 920.0,790.0,690.0,550.0,460.0,390.0,340.0,270.0,230.0,200.0,170.0,140.0,110.0,98.0,85.0,68.0,57.0,49.0,43.0,34.0, + 28.0,25.0,22.0,18.0,14.0,12.0,11.0,8.5,7.1,6.1,5.4,4.3,3.6,3.1}; +static UINT32 FNS_Table[0x400]; +static INT32 EG_TABLE[0x400]; + +typedef enum {ATTACK,DECAY1,DECAY2,RELEASE} _STATE; +struct _EG +{ + int volume; // + _STATE state; + int step; + //step vals + int AR; //Attack + int D1R; //Decay1 + int D2R; //Decay2 + int RR; //Release + + int DL; //Decay level + UINT8 LPLINK; +}; + +struct _SLOT +{ + union + { + UINT16 data[0x40]; //only 0x1a bytes used + UINT8 datab[0x80]; + } udata; + UINT8 active; //this slot is currently playing + UINT8 *base; //samples base address + UINT32 prv_addr; // previous play address (for ADPCM) + UINT32 cur_addr; //current play address (24.8) + UINT32 nxt_addr; //next play address + UINT32 step; //pitch step (24.8) + UINT8 Backwards; //the wave is playing backwards + struct _EG EG; //Envelope + struct _EG FEG; //filter envelope + struct _LFO PLFO; //Phase LFO + struct _LFO ALFO; //Amplitude LFO + int slot; + int cur_sample; //current ADPCM sample + int cur_quant; //current ADPCM step + int curstep; + int cur_lpquant, cur_lpsample, cur_lpstep; + UINT8 *adbase, *adlpbase; + UINT8 mslc; // monitored? +}; + + +#define MEM4B(aica) ((aica->udata.data[0]>>0x0)&0x0200) +#define DAC18B(aica) ((aica->udata.data[0]>>0x0)&0x0100) +#define MVOL(aica) ((aica->udata.data[0]>>0x0)&0x000F) +#define RBL(aica) ((aica->udata.data[2]>>0xD)&0x0003) +#define RBP(aica) ((aica->udata.data[2]>>0x0)&0x0fff) +#define MOFULL(aica) ((aica->udata.data[4]>>0x0)&0x1000) +#define MOEMPTY(aica) ((aica->udata.data[4]>>0x0)&0x0800) +#define MIOVF(aica) ((aica->udata.data[4]>>0x0)&0x0400) +#define MIFULL(aica) ((aica->udata.data[4]>>0x0)&0x0200) +#define MIEMPTY(aica) ((aica->udata.data[4]>>0x0)&0x0100) + +#define AFSEL(aica) ((aica->udata.data[6]>>0x0)&0x4000) +#define MSLC(aica) ((aica->udata.data[6]>>0x8)&0x3F) + +#define SCILV0(aica) ((aica->udata.data[0xa8/2]>>0x0)&0xff) +#define SCILV1(aica) ((aica->udata.data[0xac/2]>>0x0)&0xff) +#define SCILV2(aica) ((aica->udata.data[0xb0/2]>>0x0)&0xff) + +#define SCIEX0 0 +#define SCIEX1 1 +#define SCIEX2 2 +#define SCIMID 3 +#define SCIDMA 4 +#define SCIIRQ 5 +#define SCITMA 6 +#define SCITMB 7 + +struct _AICA +{ + union + { + UINT16 data[0xc0/2]; + UINT8 datab[0xc0]; + } udata; + UINT16 IRQL, IRQR; + UINT16 EFSPAN[0x48]; + struct _SLOT Slots[64]; + signed short RINGBUF[64]; + unsigned char BUFPTR; + unsigned char *AICARAM; + UINT32 AICARAM_LENGTH; + char Master; + void (*IntARMCB)(int irq); + + INT32 *buffertmpl, *buffertmpr; + + UINT32 IrqTimA; + UINT32 IrqTimBC; + UINT32 IrqMidi; + + UINT8 MidiOutW,MidiOutR; + UINT8 MidiStack[16]; + UINT8 MidiW,MidiR; + + int LPANTABLE[0x20000]; + int RPANTABLE[0x20000]; + + int TimPris[3]; + int TimCnt[3]; + + // DMA stuff + UINT32 aica_dmea; + UINT16 aica_drga; + UINT16 aica_dtlg; + + int ARTABLE[64], DRTABLE[64]; + + struct _AICADSP DSP; +}; + +static struct _AICA *AllocedAICA; + +static const float SDLT[16]={-1000000.0,-42.0,-39.0,-36.0,-33.0,-30.0,-27.0,-24.0,-21.0,-18.0,-15.0,-12.0,-9.0,-6.0,-3.0,0.0}; + +static INT16 *bufferl; +static INT16 *bufferr; + +static int length; + +static signed short *RBUFDST; //this points to where the sample will be stored in the RingBuf + +static unsigned char DecodeSCI(struct _AICA *AICA, unsigned char irq) +{ + unsigned char SCI=0; + unsigned char v; + v=(SCILV0((AICA))&(1<<irq))?1:0; + SCI|=v; + v=(SCILV1((AICA))&(1<<irq))?1:0; + SCI|=v<<1; + v=(SCILV2((AICA))&(1<<irq))?1:0; + SCI|=v<<2; + return SCI; +} + +static void ResetInterrupts(struct _AICA *AICA) +{ +#if 0 + UINT32 reset = AICA->udata.data[0xa4/2]; + if (reset & 0x40) + AICA->IntARMCB(-AICA->IrqTimA); + if (reset & 0x180) + AICA->IntARMCB(-AICA->IrqTimBC); +#endif +} + +static void CheckPendingIRQ(struct _AICA *AICA) +{ + UINT32 pend=AICA->udata.data[0xa0/2]; + UINT32 en=AICA->udata.data[0x9c/2]; + if(AICA->MidiW!=AICA->MidiR) + { + AICA->IRQL = AICA->IrqMidi; + AICA->IntARMCB(1); + return; + } + if(!pend) + return; + if(pend&0x40) + if(en&0x40) + { + AICA->IRQL = AICA->IrqTimA; + AICA->IntARMCB(1); + return; + } + if(pend&0x80) + if(en&0x80) + { + AICA->IRQL = AICA->IrqTimBC; + AICA->IntARMCB(1); + return; + } + if(pend&0x100) + if(en&0x100) + { + AICA->IRQL = AICA->IrqTimBC; + AICA->IntARMCB(1); + return; + } +} + +static int Get_AR(struct _AICA *AICA,int base,int R) +{ + int Rate=base+(R<<1); + if(Rate>63) Rate=63; + if(Rate<0) Rate=0; + return AICA->ARTABLE[Rate]; +} + +static int Get_DR(struct _AICA *AICA,int base,int R) +{ + int Rate=base+(R<<1); + if(Rate>63) Rate=63; + if(Rate<0) Rate=0; + return AICA->DRTABLE[Rate]; +} + +static int Get_RR(struct _AICA *AICA,int base,int R) +{ + int Rate=base+(R<<1); + if(Rate>63) Rate=63; + if(Rate<0) Rate=0; + return AICA->DRTABLE[Rate]; +} + +static void Compute_EG(struct _AICA *AICA,struct _SLOT *slot) +{ + int octave=OCT(slot); + int rate; + if(octave&8) octave=octave-16; + if(KRS(slot)!=0xf) + rate=octave+2*KRS(slot)+((FNS(slot)>>9)&1); + else + rate=0; //rate=((FNS(slot)>>9)&1); + + slot->EG.volume=0x17f<<EG_SHIFT; + slot->EG.AR=Get_AR(AICA,rate,AR(slot)); + slot->EG.D1R=Get_DR(AICA,rate,D1R(slot)); + slot->EG.D2R=Get_DR(AICA,rate,D2R(slot)); + slot->EG.RR=Get_RR(AICA,rate,RR(slot)); + slot->EG.DL=0x1f-DL(slot); +} + +static void AICA_StopSlot(struct _SLOT *slot,int keyoff); + +static int EG_Update(struct _SLOT *slot) +{ + switch(slot->EG.state) + { + case ATTACK: + slot->EG.volume+=slot->EG.AR; + if(slot->EG.volume>=(0x3ff<<EG_SHIFT)) + { + if (!LPSLNK(slot)) + { + slot->EG.state=DECAY1; + if(slot->EG.D1R>=(1024<<EG_SHIFT)) //Skip DECAY1, go directly to DECAY2 + slot->EG.state=DECAY2; + } + slot->EG.volume=0x3ff<<EG_SHIFT; + } + break; + case DECAY1: + slot->EG.volume-=slot->EG.D1R; + if(slot->EG.volume<=0) + slot->EG.volume=0; + if(slot->EG.volume>>(EG_SHIFT+5)<slot->EG.DL) + slot->EG.state=DECAY2; + break; + case DECAY2: + if(D2R(slot)==0) + return (slot->EG.volume>>EG_SHIFT)<<(SHIFT-10); + slot->EG.volume-=slot->EG.D2R; + if(slot->EG.volume<=0) + slot->EG.volume=0; + + break; + case RELEASE: + slot->EG.volume-=slot->EG.RR; + if(slot->EG.volume<=0) + { + slot->EG.volume=0; + AICA_StopSlot(slot,0); +// slot->EG.volume=0x17f<<EG_SHIFT; +// slot->EG.state=ATTACK; + } + break; + default: + return 1<<SHIFT; + } + return (slot->EG.volume>>EG_SHIFT)<<(SHIFT-10); +} + +static UINT32 AICA_Step(struct _SLOT *slot) +{ + int octave=OCT(slot); + UINT32 Fn; + + Fn=(FNS_Table[FNS(slot)]); //24.8 + if(octave&8) + Fn>>=(16-octave); + else + Fn<<=octave; + + return Fn/(44100); +} + + +static void Compute_LFO(struct _SLOT *slot) +{ + if(PLFOS(slot)!=0) + AICALFO_ComputeStep(&(slot->PLFO),LFOF(slot),PLFOWS(slot),PLFOS(slot),0); + if(ALFOS(slot)!=0) + AICALFO_ComputeStep(&(slot->ALFO),LFOF(slot),ALFOWS(slot),ALFOS(slot),1); +} + +#define ADPCMSHIFT 8 +#define ADFIX(f) (int) ((float) f*(float) (1<<ADPCMSHIFT)) + +const int TableQuant[8]={ADFIX(0.8984375),ADFIX(0.8984375),ADFIX(0.8984375),ADFIX(0.8984375),ADFIX(1.19921875),ADFIX(1.59765625),ADFIX(2.0),ADFIX(2.3984375)}; +const int quant_mul[16]= { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15}; + +void InitADPCM(int *PrevSignal, int *PrevQuant) +{ + *PrevSignal=0; + *PrevQuant=0x7f; +} + +INLINE signed short DecodeADPCM(int *PrevSignal, unsigned char Delta, int *PrevQuant) +{ + int x = *PrevQuant * quant_mul [Delta & 15]; + x = *PrevSignal + ((int)(x + ((UINT32)x >> 29)) >> 3); + *PrevSignal=ICLIP16(x); + *PrevQuant=(*PrevQuant*TableQuant[Delta&7])>>ADPCMSHIFT; + *PrevQuant=(*PrevQuant<0x7f)?0x7f:((*PrevQuant>0x6000)?0x6000:*PrevQuant); + return *PrevSignal; +} + +static void AICA_StartSlot(struct _AICA *AICA, struct _SLOT *slot) +{ + UINT64 start_offset; + + slot->active=1; + slot->Backwards=0; + slot->cur_addr=0; slot->nxt_addr=1<<SHIFT; slot->prv_addr=-1; + start_offset = SA(slot); // AICA can play 16-bit samples from any boundry + slot->base=&AICA->AICARAM[start_offset]; + slot->step=AICA_Step(slot); + Compute_EG(AICA,slot); + slot->EG.state=ATTACK; + slot->EG.volume=0x17f<<EG_SHIFT; + Compute_LFO(slot); + + if (PCMS(slot) >= 2) + { + UINT8 *base; + UINT32 curstep, steps_to_go; + + slot->curstep = 0; + slot->adbase = (unsigned char *) (AICA->AICARAM+((SA(slot))&0x7fffff)); + InitADPCM(&(slot->cur_sample), &(slot->cur_quant)); + InitADPCM(&(slot->cur_lpsample), &(slot->cur_lpquant)); + + // walk to the ADPCM state at LSA + curstep = 0; + base = slot->adbase; + steps_to_go = LSA(slot); + + while (curstep < steps_to_go) + { + int shift1, delta1; + shift1 = 4*((curstep&1)); + delta1 = (*base>>shift1)&0xf; + DecodeADPCM(&(slot->cur_lpsample),delta1,&(slot->cur_lpquant)); + curstep++; + if (!(curstep & 1)) + { + base++; + } + } + + slot->cur_lpstep = curstep; + slot->adlpbase = base; + + // on real hardware this creates undefined behavior. + if (LSA(slot) > LEA(slot)) + { + slot->udata.data[0xc/2] = 0xffff; + } + } +} + +static void AICA_StopSlot(struct _SLOT *slot,int keyoff) +{ + if(keyoff /*&& slot->EG.state!=RELEASE*/) + { + slot->EG.state=RELEASE; + } + else + { + slot->active=0; + } + slot->udata.data[0]&=~0x4000; + +} + +#define log_base_2(n) (log((float) n)/log((float) 2)) + +static void AICA_Init(struct _AICA *AICA, const struct AICAinterface *intf) +{ + int i=0; + + AICA->IrqTimA = AICA->IrqTimBC = AICA->IrqMidi = 0; + AICA->MidiR=AICA->MidiW=0; + AICA->MidiOutR=AICA->MidiOutW=0; + + // get AICA RAM + { + memset(AICA,0,sizeof(*AICA)); + + if (!i) + { + AICA->Master=1; + } + else + { + AICA->Master=0; + } + + if (intf->region) + { + AICA->AICARAM = &dc_ram[0]; + AICA->AICARAM_LENGTH = 2*1024*1024; + AICA->DSP.AICARAM = (UINT16 *)AICA->AICARAM; + AICA->DSP.AICARAM_LENGTH = (2*1024*1024)/2; + } + } + + for(i=0;i<0x400;++i) + { + float fcent=(double) 1200.0*log_base_2((double)(((double) 1024.0+(double)i)/(double)1024.0)); + fcent=(double) 44100.0*pow(2.0,fcent/1200.0); + FNS_Table[i]=(float) (1<<SHIFT) *fcent; + } + + for(i=0;i<0x400;++i) + { + float envDB=((float)(3*(i-0x3ff)))/32.0; + float scale=(float)(1<<SHIFT); + EG_TABLE[i]=(INT32)(pow(10.0,envDB/20.0)*scale); + } + + for(i=0;i<0x20000;++i) + { + int iTL =(i>>0x0)&0xff; + int iPAN=(i>>0x8)&0x1f; + int iSDL=(i>>0xD)&0x0F; + float TL=1.0; + float SegaDB=0; + float fSDL=1.0; + float PAN=1.0; + float LPAN,RPAN; + + if(iTL&0x01) SegaDB-=0.4; + if(iTL&0x02) SegaDB-=0.8; + if(iTL&0x04) SegaDB-=1.5; + if(iTL&0x08) SegaDB-=3; + if(iTL&0x10) SegaDB-=6; + if(iTL&0x20) SegaDB-=12; + if(iTL&0x40) SegaDB-=24; + if(iTL&0x80) SegaDB-=48; + + TL=pow(10.0,SegaDB/20.0); + + SegaDB=0; + if(iPAN&0x1) SegaDB-=3; + if(iPAN&0x2) SegaDB-=6; + if(iPAN&0x4) SegaDB-=12; + if(iPAN&0x8) SegaDB-=24; + + if((iPAN&0xf)==0xf) PAN=0.0; + else PAN=pow(10.0,SegaDB/20.0); + + if(iPAN<0x10) + { + LPAN=PAN; + RPAN=1.0; + } + else + { + RPAN=PAN; + LPAN=1.0; + } + + if(iSDL) + fSDL=pow(10.0,(SDLT[iSDL])/20.0); + else + fSDL=0.0; + + AICA->LPANTABLE[i]=FIX((4.0*LPAN*TL*fSDL)); + AICA->RPANTABLE[i]=FIX((4.0*RPAN*TL*fSDL)); + } + + AICA->ARTABLE[0]=AICA->DRTABLE[0]=0; //Infinite time + AICA->ARTABLE[1]=AICA->DRTABLE[1]=0; //Infinite time + for(i=2;i<64;++i) + { + double t,step,scale; + t=ARTimes[i]; //In ms + if(t!=0.0) + { + step=(1023*1000.0)/((float) 44100.0f*t); + scale=(double) (1<<EG_SHIFT); + AICA->ARTABLE[i]=(int) (step*scale); + } + else + AICA->ARTABLE[i]=1024<<EG_SHIFT; + + t=DRTimes[i]; //In ms + step=(1023*1000.0)/((float) 44100.0f*t); + scale=(double) (1<<EG_SHIFT); + AICA->DRTABLE[i]=(int) (step*scale); + } + + // make sure all the slots are off + for(i=0;i<64;++i) + { + AICA->Slots[i].slot=i; + AICA->Slots[i].active=0; + AICA->Slots[i].base=NULL; + AICA->Slots[i].EG.state=RELEASE; + AICA->Slots[i].mslc=0; + } + + AICALFO_Init(); + AICA->buffertmpl=(signed int*) malloc(44100*sizeof(signed int)); + AICA->buffertmpr=(signed int*) malloc(44100*sizeof(signed int)); + memset(AICA->buffertmpl,0,44100*sizeof(signed int)); + memset(AICA->buffertmpr,0,44100*sizeof(signed int)); + + // no "pend" + AICA[0].udata.data[0xa0/2] = 0; + //AICA[1].udata.data[0x20/2] = 0; + AICA->TimCnt[0] = 0xffff; + AICA->TimCnt[1] = 0xffff; + AICA->TimCnt[2] = 0xffff; +} + +static void AICA_UpdateSlotReg(struct _AICA *AICA,int s,int r) +{ + struct _SLOT *slot=AICA->Slots+s; + int sl; + switch(r&0x7f) + { + case 0: + case 1: + if(KEYONEX(slot)) + { + for(sl=0;sl<64;++sl) + { + struct _SLOT *s2=AICA->Slots+sl; + { + if(KEYONB(s2) && s2->EG.state==RELEASE/*&& !s2->active*/) + { + if(s2->mslc) AICA->udata.data[0x10] &= 0x7FFF; // reset LP at KEY_ON + AICA_StartSlot(AICA, s2); + + #if 0 + printf("StartSlot[%02X]: SSCTL %01X SA %06X LSA %04X LEA %04X PCMS %01X LPCTL %01X\n",sl,SSCTL(s2),SA(s2),LSA(s2),LEA(s2),PCMS(s2),LPCTL(s2)); + printf(" AR %02X D1R %02X D2R %02X RR %02X DL %02X KRS %01X LPSLNK %01X\n",AR(s2),D1R(s2),D2R(s2),RR(s2),DL(s2),KRS(s2),LPSLNK(s2)>>14); + printf(" TL %02X OCT %01X FNS %03X\n",TL(s2),OCT(s2),FNS(s2)); + printf(" LFORE %01X LFOF %02X ALFOWS %01X ALFOS %01X PLFOWS %01X PLFOS %01X\n",LFORE(s2),LFOF(s2),ALFOWS(s2),ALFOS(s2),PLFOWS(s2),PLFOS(s2)); + printf(" IMXL %01X ISEL %01X DISDL %01X DIPAN %02X\n",IMXL(s2),ISEL(s2),DISDL(s2),DIPAN(s2)); + printf("\n"); + fflush(stdout); + #endif + } + if(!KEYONB(s2) /*&& s2->active*/) + { + AICA_StopSlot(s2,1); + } + } + } + slot->udata.data[0]&=~0x8000; + } + break; + case 0x18: + case 0x19: + slot->step=AICA_Step(slot); + break; + case 0x14: + case 0x15: + slot->EG.RR=Get_RR(AICA,0,RR(slot)); + slot->EG.DL=0x1f-DL(slot); + break; + case 0x1c: + case 0x1d: + Compute_LFO(slot); + break; + case 0x24: +// printf("[%02d]: %x to DISDL/DIPAN (PC=%x)\n", s, slot->udata.data[0x24/2], arm7_get_register(15)); + break; + } +} + +static void AICA_UpdateReg(struct _AICA *AICA, int reg) +{ + switch(reg&0xff) + { + case 0x4: + case 0x5: + { + unsigned int v=RBL(AICA); + AICA->DSP.RBP=RBP(AICA); + if(v==0) + AICA->DSP.RBL=8*1024; + else if(v==1) + AICA->DSP.RBL=16*1024; + else if(v==2) + AICA->DSP.RBL=32*1024; + else if(v==3) + AICA->DSP.RBL=64*1024; + } + break; + case 0x8: + case 0x9: + AICA_MidiIn(0, AICA->udata.data[0x8/2]&0xff, 0); + break; + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + break; + case 0x90: + case 0x91: + if(AICA->Master) + { + AICA->TimPris[0]=1<<((AICA->udata.data[0x90/2]>>8)&0x7); + AICA->TimCnt[0]=(AICA->udata.data[0x90/2]&0xff)<<8; + } + break; + case 0x94: + case 0x95: + if(AICA->Master) + { + AICA->TimPris[1]=1<<((AICA->udata.data[0x94/2]>>8)&0x7); + AICA->TimCnt[1]=(AICA->udata.data[0x94/2]&0xff)<<8; + } + break; + case 0x98: + case 0x99: + if(AICA->Master) + { + AICA->TimPris[2]=1<<((AICA->udata.data[0x98/2]>>8)&0x7); + AICA->TimCnt[2]=(AICA->udata.data[0x98/2]&0xff)<<8; + } + break; + case 0xa4: //SCIRE + case 0xa5: + + if(AICA->Master) + { + AICA->udata.data[0xa0/2] &= ~AICA->udata.data[0xa4/2]; + ResetInterrupts(AICA); + + // behavior from real hardware (SCSP, assumed to carry over): if you SCIRE a timer that's expired, + // it'll immediately pop up again + if (AICA->TimCnt[0] >= 0xff00) + { + AICA->udata.data[0xa0/2] |= 0x40; + } + if (AICA->TimCnt[1] >= 0xff00) + { + AICA->udata.data[0xa0/2] |= 0x80; + } + if (AICA->TimCnt[2] >= 0xff00) + { + AICA->udata.data[0xa0/2] |= 0x100; + } + } + break; + case 0xa8: + case 0xa9: + case 0xac: + case 0xad: + case 0xb0: + case 0xb1: + if(AICA->Master) + { + AICA->IrqTimA=DecodeSCI(AICA,SCITMA); + AICA->IrqTimBC=DecodeSCI(AICA,SCITMB); + AICA->IrqMidi=DecodeSCI(AICA,SCIMID); + } + break; + } +} + +static void AICA_UpdateSlotRegR(struct _AICA *AICA, int slot,int reg) +{ + +} + +static void AICA_UpdateRegR(struct _AICA *AICA, int reg) +{ + switch(reg&0xff) + { + case 8: + case 9: + { + unsigned short v=AICA->udata.data[0x8/2]; + v&=0xff00; + v|=AICA->MidiStack[AICA->MidiR]; + AICA->IntARMCB(0); // cancel the IRQ + if(AICA->MidiR!=AICA->MidiW) + { + ++AICA->MidiR; + AICA->MidiR&=15; + } + AICA->udata.data[0x8/2]=v; + } + break; + + case 0x10: // LP check + case 0x11: + { + //int MSLC = (AICA->udata.data[0xc/2]>>8) & 0x3f; // which slot are we monitoring? + } + break; + + case 0x14: // CA (slot address) + case 0x15: + { + int MSLC = (AICA->udata.data[0xc/2]>>8) & 0x3f; // which slot are we monitoring? + unsigned int CA = AICA->Slots[MSLC].cur_addr>>(SHIFT+12); + + AICA->udata.data[0x14/2] = CA; + } + break; + } +} + +static void AICA_w16(struct _AICA *AICA,unsigned int addr,unsigned short val) +{ + addr&=0xffff; + if(addr<0x2000) + { + int slot=addr/0x80; + addr&=0x7f; +// printf("%x to slot %d offset %x\n", val, slot, addr); + *((unsigned short *) (AICA->Slots[slot].udata.datab+(addr))) = val; + AICA_UpdateSlotReg(AICA,slot,addr&0x7f); + } + else if (addr < 0x2800) + { + if (addr <= 0x2044) + { +// printf("%x to EFSxx slot %d (addr %x)\n", val, (addr-0x2000)/4, addr&0x7f); + AICA->EFSPAN[addr&0x7f] = val; + } + } + else if(addr<0x3000) + { + if (addr < 0x28be) + { +// printf("%x to AICA global @ %x\n", val, addr & 0xff); + *((unsigned short *) (AICA->udata.datab+((addr&0xff)))) = val; + AICA_UpdateReg(AICA, addr&0xff); + } + else if (addr == 0x2d00) + { + AICA->IRQL = val; + } + else if (addr == 0x2d04) + { + AICA->IRQR = val; + + if (val) + { + AICA->IntARMCB(0); + } + } + } + else + { + //DSP + if(addr<0x3200) //COEF + *((unsigned short *) (AICA->DSP.COEF+(addr-0x3000)/2))=val; + else if(addr<0x3400) + *((unsigned short *) (AICA->DSP.MADRS+(addr-0x3200)/2))=val; + else if(addr<0x3c00) + { + *((unsigned short *) (AICA->DSP.MPRO+(addr-0x3400)/2))=val; + + if (addr == 0x3bfe) + { + AICADSP_Start(&AICA->DSP); + } + } + } +} + +static unsigned short AICA_r16(struct _AICA *AICA, unsigned int addr) +{ + unsigned short v=0; + addr&=0xffff; + if(addr<0x2000) + { + int slot=addr/0x80; + addr&=0x7f; + AICA_UpdateSlotRegR(AICA, slot,addr&0x7f); + v=*((unsigned short *) (AICA->Slots[slot].udata.datab+(addr))); + } + else if(addr<0x3000) + { + if (addr <= 0x2044) + { + v = AICA->EFSPAN[addr&0x7f]; + } + else if (addr < 0x28be) + { + AICA_UpdateRegR(AICA, addr&0xff); + v= *((unsigned short *) (AICA->udata.datab+((addr&0xff)))); + if((addr&0xfe)==0x10) AICA->udata.data[0x10/2] &= 0x7FFF; // reset LP on read + } + else if (addr == 0x2d00) + { + return AICA->IRQL; + } + else if (addr == 0x2d04) + { + return AICA->IRQR; + } + } +// else if (addr<0x700) +// v=AICA->RINGBUF[(addr-0x600)/2]; + return v; +} + + +#define REVSIGN(v) ((~v)+1) + +void AICA_TimersAddTicks(struct _AICA *AICA, int ticks) +{ + if(AICA->TimCnt[0]<=0xff00) + { + AICA->TimCnt[0] += ticks << (8-((AICA->udata.data[0x90/2]>>8)&0x7)); + if (AICA->TimCnt[0] >= 0xFF00) + { + AICA->TimCnt[0] = 0xFFFF; + AICA->udata.data[0xa0/2]|=0x40; + } + AICA->udata.data[0x90/2]&=0xff00; + AICA->udata.data[0x90/2]|=AICA->TimCnt[0]>>8; + } + + if(AICA->TimCnt[1]<=0xff00) + { + AICA->TimCnt[1] += ticks << (8-((AICA->udata.data[0x94/2]>>8)&0x7)); + if (AICA->TimCnt[1] >= 0xFF00) + { + AICA->TimCnt[1] = 0xFFFF; + AICA->udata.data[0xa0/2]|=0x80; + } + AICA->udata.data[0x94/2]&=0xff00; + AICA->udata.data[0x94/2]|=AICA->TimCnt[1]>>8; + } + + if(AICA->TimCnt[2]<=0xff00) + { + AICA->TimCnt[2] += ticks << (8-((AICA->udata.data[0x98/2]>>8)&0x7)); + if (AICA->TimCnt[2] >= 0xFF00) + { + AICA->TimCnt[2] = 0xFFFF; + AICA->udata.data[0xa0/2]|=0x100; + } + AICA->udata.data[0x98/2]&=0xff00; + AICA->udata.data[0x98/2]|=AICA->TimCnt[2]>>8; + } +} + +INLINE INT32 AICA_UpdateSlot(struct _AICA *AICA, struct _SLOT *slot) +{ + INT32 sample, fpart; + int cur_sample; //current sample + int nxt_sample; //next sample + int step=slot->step; + UINT32 addr1,addr2; // current and next sample addresses + + if(SSCTL(slot)!=0) //no FM or noise yet + return 0; + + if(PLFOS(slot)!=0) + { + step=step*AICAPLFO_Step(&(slot->PLFO)); + step>>=SHIFT; + } + + if(PCMS(slot) == 0) + { + addr1=(slot->cur_addr>>(SHIFT-1))&0x7ffffe; + addr2=(slot->nxt_addr>>(SHIFT-1))&0x7ffffe; + } + else + { + addr1=slot->cur_addr>>SHIFT; + addr2=slot->nxt_addr>>SHIFT; + } + + if(PCMS(slot) == 1) // 8-bit signed + { + INT8 *p1=(signed char *) (AICA->AICARAM+(((SA(slot)+addr1))&0x7fffff)); + INT8 *p2=(signed char *) (AICA->AICARAM+(((SA(slot)+addr2))&0x7fffff)); + cur_sample = p1[0] << 8; + nxt_sample = p2[0] << 8; + } + else if (PCMS(slot) == 0) //16 bit signed + { + INT16 *p1=(signed short *) (AICA->AICARAM+((SA(slot)+addr1)&0x7fffff)); + INT16 *p2=(signed short *) (AICA->AICARAM+((SA(slot)+addr2)&0x7fffff)); + cur_sample = LE16(p1[0]); + nxt_sample = LE16(p2[0]); + } + else // 4-bit ADPCM + { + UINT8 *base= slot->adbase; + UINT32 steps_to_go = addr2, curstep = slot->curstep; + + if (base) + { + cur_sample = slot->cur_sample; // may already contains current decoded sample + + // seek to the interpolation sample + while (curstep < steps_to_go) + { + int shift1, delta1; + shift1 = 4*((curstep&1)); + delta1 = (*base>>shift1)&0xf; + DecodeADPCM(&(slot->cur_sample),delta1,&(slot->cur_quant)); + curstep++; + if (!(curstep & 1)) + { + base++; + } + if (curstep == addr1) + cur_sample = slot->cur_sample; + } + nxt_sample = slot->cur_sample; + + slot->adbase = base; + slot->curstep = curstep; + } + else + { + cur_sample = nxt_sample = 0; + } + } + fpart = slot->cur_addr & ((1<<SHIFT)-1); + sample=cur_sample*((1<<SHIFT)-fpart)+nxt_sample*fpart; + sample>>=SHIFT; + + slot->prv_addr=slot->cur_addr; + slot->cur_addr+=step; + slot->nxt_addr=slot->cur_addr+(1<<SHIFT); + + addr1=slot->cur_addr>>SHIFT; + addr2=slot->nxt_addr>>SHIFT; + + if(addr1>=LSA(slot)) + { + if(LPSLNK(slot) && slot->EG.state==ATTACK) + slot->EG.state = DECAY1; + } + + switch(LPCTL(slot)) + { + case 0: //no loop + if(addr2>=LSA(slot) && addr2>=LEA(slot)) // if next sample exceed then current must exceed too + { + //slot->active=0; + if(slot->mslc) AICA->udata.data[8] |= 0x8000; + AICA_StopSlot(slot,0); + } + break; + case 1: //normal loop + if(addr2>=LEA(slot)) + { + INT32 rem_addr; + if(slot->mslc) AICA->udata.data[8] |= 0x8000; + rem_addr = slot->nxt_addr - (LEA(slot)<<SHIFT); + slot->nxt_addr = (LSA(slot)<<SHIFT) + rem_addr; + if(addr1>=LEA(slot)) + { + rem_addr = slot->cur_addr - (LEA(slot)<<SHIFT); + slot->cur_addr = (LSA(slot)<<SHIFT) + rem_addr; + } + + if(PCMS(slot)>=2) + { + // restore the state @ LSA - the sampler will naturally walk to (LSA + remainder) + slot->adbase = &AICA->AICARAM[SA(slot)+(LSA(slot)/2)]; + slot->curstep = LSA(slot); + if (PCMS(slot) == 2) + { + slot->cur_sample = slot->cur_lpsample; + slot->cur_quant = slot->cur_lpquant; + } +//printf("Looping: slot_addr %x LSA %x LEA %x step %x base %x\n", slot->cur_addr>>SHIFT, LSA(slot), LEA(slot), slot->curstep, slot->adbase); + } + } + break; + } + + if(ALFOS(slot)!=0) + { + sample=sample*AICAALFO_Step(&(slot->ALFO)); + sample>>=SHIFT; + } + + if(slot->EG.state==ATTACK) + sample=(sample*EG_Update(slot))>>SHIFT; + else + sample=(sample*EG_TABLE[EG_Update(slot)>>(SHIFT-10)])>>SHIFT; + + if(slot->mslc) + { + AICA->udata.data[0x14/2] = addr1; + if (!(AFSEL(AICA))) + { + UINT16 res; + + AICA->udata.data[0x10/2] |= slot->EG.state<<13; + + res = 0x3FF - (slot->EG.volume>>EG_SHIFT); + + res *= 959; + res /= 1024; + + if (res > 959) res = 959; + + AICA->udata.data[0x10/2] = res; + + //AICA->udata.data[0x10/2] |= 0x3FF - (slot->EG.volume>>EG_SHIFT); + } + } + + return sample; +} + +static void AICA_DoMasterSamples(struct _AICA *AICA, int nsamples) +{ + INT16 *bufr,*bufl; + int sl, s, i; + + bufr=bufferr; + bufl=bufferl; + + for(s=0;s<nsamples;++s) + { + INT32 smpl, smpr; + + smpl = smpr = 0; + + // mix slots' direct output + for(sl=0;sl<64;++sl) + { + struct _SLOT *slot=AICA->Slots+sl; + slot->mslc = (MSLC(AICA)==sl); + RBUFDST=AICA->RINGBUF+AICA->BUFPTR; + if(AICA->Slots[sl].active) + { + unsigned int Enc; + signed int sample; + + sample=AICA_UpdateSlot(AICA, slot); + + Enc=((TL(slot))<<0x0)|((IMXL(slot))<<0xd); + AICADSP_SetSample(&AICA->DSP,(sample*AICA->LPANTABLE[Enc])>>(SHIFT-2),ISEL(slot),IMXL(slot)); + Enc=((TL(slot))<<0x0)|((DIPAN(slot))<<0x8)|((DISDL(slot))<<0xd); + { + smpl+=(sample*AICA->LPANTABLE[Enc])>>SHIFT; + smpr+=(sample*AICA->RPANTABLE[Enc])>>SHIFT; + } + } + + AICA->BUFPTR&=63; + } + + // process the DSP + AICADSP_Step(&AICA->DSP); + + // mix DSP output + for(i=0;i<16;++i) + { + if(EFSDL(i)) + { + unsigned int Enc=((EFPAN(i))<<0x8)|((EFSDL(i))<<0xd); + smpl+=(AICA->DSP.EFREG[i]*AICA->LPANTABLE[Enc])>>SHIFT; + smpr+=(AICA->DSP.EFREG[i]*AICA->RPANTABLE[Enc])>>SHIFT; + } + } + + *bufl++ = ICLIP16(smpl>>3); + *bufr++ = ICLIP16(smpr>>3); + + AICA_TimersAddTicks(AICA, 1); + CheckPendingIRQ(AICA); + } +} + +int AICA_IRQCB(void *param) +{ + CheckPendingIRQ(param); + return -1; +} + +void AICA_Update(void *param, INT16 **inputs, INT16 **buf, int samples) +{ + struct _AICA *AICA = AllocedAICA; + bufferl = buf[0]; + bufferr = buf[1]; + length = samples; + AICA_DoMasterSamples(AICA, samples); +} + +void *aica_start(const void *config) +{ + const struct AICAinterface *intf; + + struct _AICA *AICA; + + AICA = malloc(sizeof(*AICA)); + memset(AICA, 0, sizeof(*AICA)); + + intf = config; + + // init the emulation + AICA_Init(AICA, intf); + + // set up the IRQ callbacks + { + AICA->IntARMCB = intf->irq_callback[0]; + +// AICA->stream = stream_create(0, 2, 44100, AICA, AICA_Update); + } + + AllocedAICA = AICA; + + return AICA; +} + +void aica_stop(void) +{ + free(AllocedAICA); +} + +void AICA_set_ram_base(int which, void *base) +{ + struct _AICA *AICA = AllocedAICA; + if (AICA) + { + AICA->AICARAM = base; + AICA->DSP.AICARAM = base; + } +} + +READ16_HANDLER( AICA_0_r ) +{ + struct _AICA *AICA = AllocedAICA; + UINT16 res = AICA_r16(AICA, offset*2); + +// printf("Read AICA @ %x => %x (PC=%x, R5=%x)\n", offset*2, res, arm7_get_register(15), arm7_get_register(5)); + + return res; +} + +extern UINT32* stv_scu; + +WRITE16_HANDLER( AICA_0_w ) +{ + struct _AICA *AICA = AllocedAICA; + UINT16 tmp; + + tmp = AICA_r16(AICA, offset*2); + COMBINE_DATA(&tmp); + AICA_w16(AICA,offset*2, tmp); +} + +WRITE16_HANDLER( AICA_MidiIn ) +{ + struct _AICA *AICA = AllocedAICA; + AICA->MidiStack[AICA->MidiW++]=data; + AICA->MidiW &= 15; +} + +READ16_HANDLER( AICA_MidiOutR ) +{ + struct _AICA *AICA = AllocedAICA; + unsigned char val; + + val=AICA->MidiStack[AICA->MidiR++]; + AICA->MidiR&=7; + return val; +} + diff --git a/plugins/ao/eng_dsf/aica.h b/plugins/ao/eng_dsf/aica.h new file mode 100644 index 00000000..f01468f3 --- /dev/null +++ b/plugins/ao/eng_dsf/aica.h @@ -0,0 +1,44 @@ +/* + + Sega/Yamaha AICA emulation +*/ + +#ifndef _AICA_H_ +#define _AICA_H_ + +#define MAX_AICA (2) + +#define COMBINE_DATA(varptr) (*(varptr) = (*(varptr) & mem_mask) | (data & ~mem_mask)) + +// convert AO types +typedef int8 data8_t; +typedef int16 data16_t; +typedef int32 data32_t; +typedef int offs_t; + +struct AICAinterface +{ + int num; + void *region[MAX_AICA]; + int mixing_level[MAX_AICA]; /* volume */
+ void (*irq_callback[MAX_AICA])(int state); /* irq callback */ +}; + +int AICA_sh_start(struct AICAinterface *intf); +void AICA_sh_stop(void); +void scsp_stop(void); + +#define READ16_HANDLER(name) data16_t name(offs_t offset, data16_t mem_mask) +#define WRITE16_HANDLER(name) void name(offs_t offset, data16_t data, data16_t mem_mask) + +// AICA register access +READ16_HANDLER( AICA_0_r ); +WRITE16_HANDLER( AICA_0_w ); +READ16_HANDLER( AICA_1_r ); +WRITE16_HANDLER( AICA_1_w ); + +// MIDI I/O access (used for comms on Model 2/3) +WRITE16_HANDLER( AICA_MidiIn ); +READ16_HANDLER( AICA_MidiOutR ); + +#endif diff --git a/plugins/ao/eng_dsf/aicadsp.c b/plugins/ao/eng_dsf/aicadsp.c new file mode 100644 index 00000000..b2fb5223 --- /dev/null +++ b/plugins/ao/eng_dsf/aicadsp.c @@ -0,0 +1,349 @@ +#include <assert.h> +#include <math.h> +#include "ao.h" +#include "cpuintrf.h" +#include "aica.h" +#include "aicadsp.h" + +static UINT16 PACK(INT32 val) +{ + UINT32 temp; + int sign,exponent,k; + + sign = (val >> 23) & 0x1; + temp = (val ^ (val << 1)) & 0xFFFFFF; + exponent = 0; + for (k=0; k<12; k++) + { + if (temp & 0x800000) + break; + temp <<= 1; + exponent += 1; + } + if (exponent < 12) + val = (val << exponent) & 0x3FFFFF; + else + val <<= 11; + val >>= 11; + val |= sign << 15; + val |= exponent << 11; + + return (UINT16)val; +} + +static INT32 UNPACK(UINT16 val) +{ + int sign,exponent,mantissa; + INT32 uval; + + sign = (val >> 15) & 0x1; + exponent = (val >> 11) & 0xF; + mantissa = val & 0x7FF; + uval = mantissa << 11; + if (exponent > 11) + exponent = 11; + else + uval |= (sign ^ 1) << 22; + uval |= sign << 23; + uval <<= 8; + uval >>= 8; + uval >>= exponent; + + return uval; +} + +void AICADSP_Init(struct _AICADSP *DSP) +{ + memset(DSP,0,sizeof(struct _AICADSP)); + DSP->RBL=0x8000; + DSP->Stopped=1; +} + +void AICADSP_Step(struct _AICADSP *DSP) +{ + INT32 ACC=0; //26 bit + INT32 SHIFTED=0; //24 bit + INT32 X=0; //24 bit + INT32 Y=0; //13 bit + INT32 B=0; //26 bit + INT32 INPUTS=0; //24 bit + INT32 MEMVAL=0; + INT32 FRC_REG=0; //13 bit + INT32 Y_REG=0; //24 bit + UINT32 ADDR=0; + UINT32 ADRS_REG=0; //13 bit + int step; + + if(DSP->Stopped) + return; + + memset(DSP->EFREG,0,2*16); +#if 0 + int dump=0; + FILE *f=NULL; + if(dump) + f=fopen("dsp.txt","wt"); +#endif + for(step=0;step</*128*/DSP->LastStep;++step) + { + UINT16 *IPtr=DSP->MPRO+step*8; + +// if(IPtr[0]==0 && IPtr[1]==0 && IPtr[2]==0 && IPtr[3]==0) +// break; + + UINT32 TRA=(IPtr[0]>>9)&0x7F; + UINT32 TWT=(IPtr[0]>>8)&0x01; + UINT32 TWA=(IPtr[0]>>1)&0x7F; + + UINT32 XSEL=(IPtr[2]>>15)&0x01; + UINT32 YSEL=(IPtr[2]>>13)&0x03; + UINT32 IRA=(IPtr[2]>>7)&0x3F; + UINT32 IWT=(IPtr[2]>>6)&0x01; + UINT32 IWA=(IPtr[2]>>1)&0x1F; + + UINT32 TABLE=(IPtr[4]>>15)&0x01; + UINT32 MWT=(IPtr[4]>>14)&0x01; + UINT32 MRD=(IPtr[4]>>13)&0x01; + UINT32 EWT=(IPtr[4]>>12)&0x01; + UINT32 EWA=(IPtr[4]>>8)&0x0F; + UINT32 ADRL=(IPtr[4]>>7)&0x01; + UINT32 FRCL=(IPtr[4]>>6)&0x01; + UINT32 SHIFT=(IPtr[4]>>4)&0x03; + UINT32 YRL=(IPtr[4]>>3)&0x01; + UINT32 NEGB=(IPtr[4]>>2)&0x01; + UINT32 ZERO=(IPtr[4]>>1)&0x01; + UINT32 BSEL=(IPtr[4]>>0)&0x01; + + UINT32 NOFL=(IPtr[6]>>15)&1; //???? + UINT32 COEF=step; + + UINT32 MASA=(IPtr[6]>>9)&0x3f; //??? + UINT32 ADREB=(IPtr[6]>>8)&0x1; + UINT32 NXADR=(IPtr[6]>>7)&0x1; + + INT64 v; + + //operations are done at 24 bit precision +#if 0 + if(MASA) + int a=1; + if(NOFL) + int a=1; + +// int dump=0; + + if(f) + { +#define DUMP(v) fprintf(f," " #v ": %04X",v); + + fprintf(f,"%d: ",step); + DUMP(ACC); + DUMP(SHIFTED); + DUMP(X); + DUMP(Y); + DUMP(B); + DUMP(INPUTS); + DUMP(MEMVAL); + DUMP(FRC_REG); + DUMP(Y_REG); + DUMP(ADDR); + DUMP(ADRS_REG); + fprintf(f,"\n"); + } +#endif + //INPUTS RW + assert(IRA<0x32); + if(IRA<=0x1f) + INPUTS=DSP->MEMS[IRA]; + else if(IRA<=0x2F) + INPUTS=DSP->MIXS[IRA-0x20]<<4; //MIXS is 20 bit + else if(IRA<=0x31) + INPUTS=0; + + INPUTS<<=8; + INPUTS>>=8; + //if(INPUTS&0x00800000) + // INPUTS|=0xFF000000; + + if(IWT) + { + DSP->MEMS[IWA]=MEMVAL; //MEMVAL was selected in previous MRD + if(IRA==IWA) + INPUTS=MEMVAL; + } + + //Operand sel + //B + if(!ZERO) + { + if(BSEL) + B=ACC; + else + { + B=DSP->TEMP[(TRA+DSP->DEC)&0x7F]; + B<<=8; + B>>=8; + //if(B&0x00800000) + // B|=0xFF000000; //Sign extend + } + if(NEGB) + B=0-B; + } + else + B=0; + + //X + if(XSEL) + X=INPUTS; + else + { + X=DSP->TEMP[(TRA+DSP->DEC)&0x7F]; + X<<=8; + X>>=8; + //if(X&0x00800000) + // X|=0xFF000000; + } + + //Y + if(YSEL==0) + Y=FRC_REG; + else if(YSEL==1) + Y=DSP->COEF[COEF<<1]>>3; //COEF is 16 bits + else if(YSEL==2) + Y=(Y_REG>>11)&0x1FFF; + else if(YSEL==3) + Y=(Y_REG>>4)&0x0FFF; + + if(YRL) + Y_REG=INPUTS; + + //Shifter + if(SHIFT==0) + { + SHIFTED=ACC; + if(SHIFTED>0x007FFFFF) + SHIFTED=0x007FFFFF; + if(SHIFTED<(-0x00800000)) + SHIFTED=-0x00800000; + } + else if(SHIFT==1) + { + SHIFTED=ACC*2; + if(SHIFTED>0x007FFFFF) + SHIFTED=0x007FFFFF; + if(SHIFTED<(-0x00800000)) + SHIFTED=-0x00800000; + } + else if(SHIFT==2) + { + SHIFTED=ACC*2; + SHIFTED<<=8; + SHIFTED>>=8; + //SHIFTED&=0x00FFFFFF; + //if(SHIFTED&0x00800000) + // SHIFTED|=0xFF000000; + } + else if(SHIFT==3) + { + SHIFTED=ACC; + SHIFTED<<=8; + SHIFTED>>=8; + //SHIFTED&=0x00FFFFFF; + //if(SHIFTED&0x00800000) + // SHIFTED|=0xFF000000; + } + + //ACCUM + Y<<=19; + Y>>=19; + //if(Y&0x1000) + // Y|=0xFFFFF000; + + v=(((INT64) X*(INT64) Y)>>12); + ACC=(int) v+B; + + if(TWT) + DSP->TEMP[(TWA+DSP->DEC)&0x7F]=SHIFTED; + + if(FRCL) + { + if(SHIFT==3) + FRC_REG=SHIFTED&0x0FFF; + else + FRC_REG=(SHIFTED>>11)&0x1FFF; + } + + if(MRD || MWT) + //if(0) + { + ADDR=DSP->MADRS[MASA<<1]; + if(!TABLE) + ADDR+=DSP->DEC; + if(ADREB) + ADDR+=ADRS_REG&0x0FFF; + if(NXADR) + ADDR++; + if(!TABLE) + ADDR&=DSP->RBL-1; + else + ADDR&=0xFFFF; + //ADDR<<=1; + //ADDR+=DSP->RBP<<13; + //MEMVAL=DSP->AICARAM[ADDR>>1]; + ADDR+=DSP->RBP<<10; + if(MRD && (step&1)) //memory only allowed on odd? DoA inserts NOPs on even + { + if(NOFL) + MEMVAL=DSP->AICARAM[ADDR]<<8; + else + MEMVAL=UNPACK(DSP->AICARAM[ADDR]); + } + if(MWT && (step&1)) + { + if(NOFL) + DSP->AICARAM[ADDR]=SHIFTED>>8; + else + DSP->AICARAM[ADDR]=PACK(SHIFTED); + } + } + + if(ADRL) + { + if(SHIFT==3) + ADRS_REG=(SHIFTED>>12)&0xFFF; + else + ADRS_REG=(INPUTS>>16); + } + + if(EWT) + DSP->EFREG[EWA]+=SHIFTED>>8; + + } + --DSP->DEC; + memset(DSP->MIXS,0,4*16); +// if(f) +// fclose(f); +} + +void AICADSP_SetSample(struct _AICADSP *DSP,INT32 sample,int SEL,int MXL) +{ + //DSP->MIXS[SEL]+=sample<<(MXL+1)/*7*/; + DSP->MIXS[SEL]+=sample; +// if(MXL) +// int a=1; +} + +void AICADSP_Start(struct _AICADSP *DSP) +{ + int i; + DSP->Stopped=0; + for(i=127;i>=0;--i) + { + UINT16 *IPtr=DSP->MPRO+i*8; + + if(IPtr[0]!=0 || IPtr[2]!=0 || IPtr[4]!=0 || IPtr[6]!=0) + break; + } + DSP->LastStep=i+1; + +} diff --git a/plugins/ao/eng_dsf/aicadsp.h b/plugins/ao/eng_dsf/aicadsp.h new file mode 100644 index 00000000..b10accff --- /dev/null +++ b/plugins/ao/eng_dsf/aicadsp.h @@ -0,0 +1,37 @@ +#ifndef AICADSP_H +#define AICADSP_H + +//the DSP Context +struct _AICADSP +{ +//Config + UINT16 *AICARAM; + UINT32 AICARAM_LENGTH; + UINT32 RBP; //Ring buf pointer + UINT32 RBL; //Delay ram (Ring buffer) size in words + +//context + + INT16 COEF[128*2]; //16 bit signed + UINT16 MADRS[64*2]; //offsets (in words), 16 bit + UINT16 MPRO[128*4*2*2]; //128 steps 64 bit + INT32 TEMP[128]; //TEMP regs,24 bit signed + INT32 MEMS[32]; //MEMS regs,24 bit signed + UINT32 DEC; + +//input + INT32 MIXS[16]; //MIXS, 24 bit signed + INT16 EXTS[2]; //External inputs (CDDA) 16 bit signed + +//output + INT16 EFREG[16]; //EFREG, 16 bit signed + + int Stopped; + int LastStep; +}; + +void AICADSP_Init(struct _AICADSP *DSP); +void AICADSP_SetSample(struct _AICADSP *DSP, INT32 sample, INT32 SEL, INT32 MXL); +void AICADSP_Step(struct _AICADSP *DSP); +void AICADSP_Start(struct _AICADSP *DSP); +#endif diff --git a/plugins/ao/eng_dsf/aicalfo.c b/plugins/ao/eng_dsf/aicalfo.c new file mode 100644 index 00000000..9af2ae54 --- /dev/null +++ b/plugins/ao/eng_dsf/aicalfo.c @@ -0,0 +1,159 @@ +/* + AICA LFO handling + + Part of the AICA emulator package. + (not compiled directly, #included from aica.c) + + By ElSemi, kingshriek, and R. Belmont +*/ + +#define LFO_SHIFT 8 + +struct _LFO +{ + unsigned short phase; + UINT32 phase_step; + int *table; + int *scale; +}; + +#define LFIX(v) ((unsigned int) ((float) (1<<LFO_SHIFT)*(v))) + +//Convert DB to multiply amplitude +#define DB(v) LFIX(pow(10.0,v/20.0)) + +//Convert cents to step increment +#define CENTS(v) LFIX(pow(2.0,v/1200.0)) + +static int PLFO_TRI[256],PLFO_SQR[256],PLFO_SAW[256],PLFO_NOI[256]; +static int ALFO_TRI[256],ALFO_SQR[256],ALFO_SAW[256],ALFO_NOI[256]; +static float LFOFreq[32]={0.17,0.19,0.23,0.27,0.34,0.39,0.45,0.55,0.68,0.78,0.92,1.10,1.39,1.60,1.87,2.27, + 2.87,3.31,3.92,4.79,6.15,7.18,8.60,10.8,14.4,17.2,21.5,28.7,43.1,57.4,86.1,172.3}; +static float ASCALE[8]={0.0,0.4,0.8,1.5,3.0,6.0,12.0,24.0}; +static float PSCALE[8]={0.0,7.0,13.5,27.0,55.0,112.0,230.0,494}; +static int PSCALES[8][256]; +static int ASCALES[8][256]; + +void AICALFO_Init(void) +{ + int i,s; + for(i=0;i<256;++i) + { + int a,p; +// float TL; + //Saw + a=255-i; + if(i<128) + p=i; + else + p=i-256; + ALFO_SAW[i]=a; + PLFO_SAW[i]=p; + + //Square + if(i<128) + { + a=255; + p=127; + } + else + { + a=0; + p=-128; + } + ALFO_SQR[i]=a; + PLFO_SQR[i]=p; + + //Tri + if(i<128) + a=255-(i*2); + else + a=(i*2)-256; + if(i<64) + p=i*2; + else if(i<128) + p=255-i*2; + else if(i<192) + p=256-i*2; + else + p=i*2-511; + ALFO_TRI[i]=a; + PLFO_TRI[i]=p; + + //noise + //a=lfo_noise[i]; + a=rand()&0xff; + p=128-a; + ALFO_NOI[i]=a; + PLFO_NOI[i]=p; + } + + for(s=0;s<8;++s) + { + float limit=PSCALE[s]; + for(i=-128;i<128;++i) + { + PSCALES[s][i+128]=CENTS(((limit*(float) i)/128.0)); + } + limit=-ASCALE[s]; + for(i=0;i<256;++i) + { + ASCALES[s][i]=DB(((limit*(float) i)/256.0)); + } + } +} + +signed int INLINE AICAPLFO_Step(struct _LFO *LFO) +{ + int p; + + LFO->phase+=LFO->phase_step; +#if LFO_SHIFT!=8 + LFO->phase&=(1<<(LFO_SHIFT+8))-1; +#endif + p=LFO->table[LFO->phase>>LFO_SHIFT]; + p=LFO->scale[p+128]; + return p<<(SHIFT-LFO_SHIFT); +} + +signed int INLINE AICAALFO_Step(struct _LFO *LFO) +{ + int p; + LFO->phase+=LFO->phase_step; +#if LFO_SHIFT!=8 + LFO->phase&=(1<<(LFO_SHIFT+8))-1; +#endif + p=LFO->table[LFO->phase>>LFO_SHIFT]; + p=LFO->scale[p]; + return p<<(SHIFT-LFO_SHIFT); +} + +void AICALFO_ComputeStep(struct _LFO *LFO,UINT32 LFOF,UINT32 LFOWS,UINT32 LFOS,int ALFO) +{ + float step=(float) LFOFreq[LFOF]*256.0/(float)44100.0; + LFO->phase_step=(unsigned int) ((float) (1<<LFO_SHIFT)*step); + if(ALFO) + { + switch(LFOWS) + { + case 0: LFO->table=ALFO_SAW; break; + case 1: LFO->table=ALFO_SQR; break; + case 2: LFO->table=ALFO_TRI; break; + case 3: LFO->table=ALFO_NOI; break; + default: printf("Unknown ALFO %d\n", LFOWS); + } + LFO->scale=ASCALES[LFOS]; + } + else + { + switch(LFOWS) + { + case 0: LFO->table=PLFO_SAW; break; + case 1: LFO->table=PLFO_SQR; break; + case 2: LFO->table=PLFO_TRI; break; + case 3: LFO->table=PLFO_NOI; break; + default: printf("Unknown PLFO %d\n", LFOWS); + } + LFO->scale=PSCALES[LFOS]; + } +} diff --git a/plugins/ao/eng_dsf/arm7.c b/plugins/ao/eng_dsf/arm7.c new file mode 100644 index 00000000..980c306f --- /dev/null +++ b/plugins/ao/eng_dsf/arm7.c @@ -0,0 +1,274 @@ +// +// ARM7 processor emulator +// version 1.6 / 2008-02-16 +// (c) Radoslaw Balcewicz +// + +#include "arm7.h" +#include "arm7i.h" + +#ifdef ARM7_THUMB +#include "arm7thumb.h" +#endif + + //-------------------------------------------------------------------------- + // definitions and macros + + /** Macro for accessing banked registers. */ +#define RX_BANK(t,r) (ARM7.Rx_bank [t][r - 8]) + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // private functions + + /** CPU Reset. */ +static void Reset (void); + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // public variables + + /** ARM7 state. */ +struct sARM7 ARM7; + + // private variables + + /** Table for decoding bit-coded mode to zero based index. */ +static const int s_tabTryb [32] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, ARM7_MODE_usr, ARM7_MODE_fiq, ARM7_MODE_irq, + ARM7_MODE_svc, -1, -1, -1, ARM7_MODE_abt, -1, -1, -1, ARM7_MODE_und, + -1, -1, -1, ARM7_MODE_sys}; + //-------------------------------------------------------------------------- + + + // public functions + + + //-------------------------------------------------------------------------- + /** ARM7 emulator init. */ +void ARM7_Init () + { + // sane startup values + ARM7.fiq = 0; + ARM7.irq = 0; + ARM7.carry = 0; + ARM7.overflow = 0; + ARM7.flagi = FALSE; + ARM7.cykle = 0; + + // reset will do the rest + ARM7_HardReset (); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Power-ON reset. */ +void ARM7_HardReset () + { + // CPSR that makes sense + ARM7.Rx [ARM7_CPSR] = ARM7_CPSR_I | ARM7_CPSR_F | ARM7_CPSR_M_svc; + Reset (); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Hardware reset via /RESET line. */ +void ARM7_SoftReset () + { + Reset (); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** CPSR update, possibly changing operating mode. */ +void ARM7_SetCPSR (ARM7_REG sr) + { + int stary, nowy; + + stary = s_tabTryb [ARM7_CPSR_M (ARM7.Rx [ARM7_CPSR])]; + nowy = s_tabTryb [ARM7_CPSR_M (sr)]; + // do we have to change modes? + if (nowy != stary) + { + // save this mode registers + RX_BANK (stary, ARM7_SP) = ARM7.Rx [ARM7_SP], + RX_BANK (stary, ARM7_LR) = ARM7.Rx [ARM7_LR], + RX_BANK (stary, ARM7_SPSR) = ARM7.Rx [ARM7_SPSR]; + if (stary == ARM7_MODE_fiq) + { + // copy R8-R12 + RX_BANK (ARM7_MODE_fiq, 8) = ARM7.Rx [8], + RX_BANK (ARM7_MODE_fiq, 9) = ARM7.Rx [9], + RX_BANK (ARM7_MODE_fiq, 10) = ARM7.Rx [10], + RX_BANK (ARM7_MODE_fiq, 11) = ARM7.Rx [11], + RX_BANK (ARM7_MODE_fiq, 12) = ARM7.Rx [12]; + ARM7.Rx [8] = RX_BANK (ARM7_MODE_usr, 8), + ARM7.Rx [9] = RX_BANK (ARM7_MODE_usr, 9), + ARM7.Rx [10] = RX_BANK (ARM7_MODE_usr, 10), + ARM7.Rx [11] = RX_BANK (ARM7_MODE_usr, 11), + ARM7.Rx [12] = RX_BANK (ARM7_MODE_usr, 12); + } + + // fetch new mode registers + ARM7.Rx [ARM7_SP] = RX_BANK (nowy, ARM7_SP), + ARM7.Rx [ARM7_LR] = RX_BANK (nowy, ARM7_LR), + ARM7.Rx [ARM7_SPSR] = RX_BANK (nowy, ARM7_SPSR); + if (nowy == ARM7_MODE_fiq) + { + // copy R8-R12 + RX_BANK (ARM7_MODE_usr, 8) = ARM7.Rx [8], + RX_BANK (ARM7_MODE_usr, 9) = ARM7.Rx [9], + RX_BANK (ARM7_MODE_usr, 10) = ARM7.Rx [10], + RX_BANK (ARM7_MODE_usr, 11) = ARM7.Rx [11], + RX_BANK (ARM7_MODE_usr, 12) = ARM7.Rx [12]; + ARM7.Rx [8] = RX_BANK (ARM7_MODE_fiq, 8), + ARM7.Rx [9] = RX_BANK (ARM7_MODE_fiq, 9), + ARM7.Rx [10] = RX_BANK (ARM7_MODE_fiq, 10), + ARM7.Rx [11] = RX_BANK (ARM7_MODE_fiq, 11), + ARM7.Rx [12] = RX_BANK (ARM7_MODE_fiq, 12); + } + } + + // new CPSR value + ARM7.Rx [ARM7_CPSR] = sr; + + // mode change could've enabled interrups, so we test for those and set + // appropriate flag for the instruction loop to catch + if (ARM7.fiq) + ARM7.flagi |= ARM7_FL_FIQ; +#ifndef ARM7_DREAMCAST + if (ARM7.irq) + ARM7.flagi |= ARM7_FL_IRQ; +#endif + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Sets FIQ line state. */ +void ARM7_SetFIQ (int stan) + { + stan = stan ? TRUE : FALSE; + // we catch changes only + if (stan ^ ARM7.fiq) + { + ARM7.fiq = stan; + if (ARM7.fiq) + ARM7.flagi |= ARM7_FL_FIQ; + } + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Sets IRQ line state. */ +void ARM7_SetIRQ (int stan) + { + stan = stan ? TRUE : FALSE; + // we catch changes only + if (stan ^ ARM7.irq) + { + ARM7.irq = stan; + if (ARM7.irq) + ARM7.flagi |= ARM7_FL_IRQ; + } + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Tests for pending interrupts, switches to one if possible. */ +void ARM7_CheckIRQ () + { + UINT32 sr = ARM7.Rx [ARM7_CPSR]; + + // clear all interrupt flags + ARM7.flagi &= ~(ARM7_FL_FIQ | ARM7_FL_IRQ); + + // check for pending interrupts we can switch to + // (FIQ can interrupt IRQ, but not the other way around) + if (ARM7.fiq) + { + if (!(sr & ARM7_CPSR_F)) + { + // FIQ + ARM7_SetCPSR (ARM7_CPSR_MX (sr, ARM7_CPSR_M_fiq) | ARM7_CPSR_F | ARM7_CPSR_I); + ARM7.Rx [ARM7_SPSR] = sr; + // set new PC (return from interrupt will subtract 4) + ARM7.Rx [ARM7_LR] = ARM7.Rx [ARM7_PC] + 4; + ARM7.Rx [ARM7_PC] = 0x0000001c; + } + } +#ifndef ARM7_DREAMCAST + if (ARM7.irq) + { + if (!(sr & ARM7_CPSR_I)) + { + // IRQ + ARM7_SetCPSR (ARM7_CPSR_MX (sr, ARM7_CPSR_M_irq) | ARM7_CPSR_I); + ARM7.Rx [ARM7_SPSR] = sr; + // set new PC (return from interrupt will subtract 4) + ARM7.Rx [ARM7_LR] = ARM7.Rx [ARM7_PC] + 4; + ARM7.Rx [ARM7_PC] = 0x00000018; + ARM7.irq = 0; + } + } +#endif + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Single step. */ +void ARM7_Step () +{ + // make a step +#ifdef ARM7_THUMB + if (ARM7.Rx[ARM7_CPSR] & ARM7_CPSR_T) + { + ARM7i_Thumb_Step(); + } + else +#endif + { + ARM7i_Step (); + } + // and test interrupts + ARM7_CheckIRQ (); +} + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Runs emulation for at least n cycles, returns actual amount of cycles + burned - normal interpreter. */ +int ARM7_Execute (int n) + { + ARM7.cykle = 0; + while (ARM7.cykle < n) + { + ARM7_CheckIRQ (); + while (!ARM7.flagi && ARM7.cykle < n) + // make one step, sum up cycles + ARM7.cykle += ARM7i_Step (); + } + return ARM7.cykle; + } + //-------------------------------------------------------------------------- + + + // private functions + + + //-------------------------------------------------------------------------- + /** CPU Reset. */ +void Reset (void) + { + // clear ALU flags + ARM7.carry = 0; + ARM7.overflow = 0; + // test CPSR mode and pick a valid one if necessary + if (s_tabTryb [ARM7_CPSR_M (ARM7.Rx [ARM7_CPSR])] < 0) + ARM7.Rx [ARM7_CPSR] = ARM7_CPSR_I | ARM7_CPSR_F | ARM7_CPSR_M_svc; + // set up registers according to manual + RX_BANK (ARM7_MODE_svc, ARM7_LR) = ARM7.Rx [ARM7_PC]; + RX_BANK (ARM7_MODE_svc, ARM7_SPSR) = ARM7.Rx [ARM7_CPSR]; + ARM7_SetCPSR (ARM7_CPSR_I | ARM7_CPSR_F | ARM7_CPSR_M_svc); + ARM7.Rx [ARM7_PC] = 0x00000000; + } + //-------------------------------------------------------------------------- diff --git a/plugins/ao/eng_dsf/arm7.h b/plugins/ao/eng_dsf/arm7.h new file mode 100644 index 00000000..41cb9b56 --- /dev/null +++ b/plugins/ao/eng_dsf/arm7.h @@ -0,0 +1,165 @@ +// +// ARM7 processor emulator +// version 1.6 / 2008-02-16 +// (c) Radoslaw Balcewicz +// + +#ifndef _ARM7_h_ +#define _ARM7_h_ + +#include "cpuintrf.h" + + //-------------------------------------------------------------------------- + // definitions and macros + + /** If defined, will turn on specific behavior emulation, as well as some + optimizations that are valid only for Dreamcast AICA. */ +#define ARM7_DREAMCAST + + /** Define to enable Thumb support for ARM7. */ +//#define ARM7_THUMB + + // sanity tests +#ifdef ARM7_DREAMCAST + #ifdef ARM7_THUMB + #warning "Dreamcast ARM7 is a -DI type, it doesn't support Thumb mode." + #endif +#else +// #warning "Instructions cycle counts might not be correct." +#endif + + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // CPU definitions + + /** Status flags in CPSR register. */ +#define ARM7_CPSR_N (1 << 31) +#define ARM7_CPSR_Z (1 << 30) +#define ARM7_CPSR_C (1 << 29) +#define ARM7_CPSR_V (1 << 28) +#define ARM7_CPSR_I (1 << 7) +#define ARM7_CPSR_F (1 << 6) +#define ARM7_CPSR_T (1 << 5) + /** CPSR bit mask for current operating mode. */ +#define ARM7_CPSR_M(x) ((x) & 0x1f) +#define ARM7_CPSR_MX(sr,x) (((sr) & ~0x1f) | ((x) & 0x1f)) + /** Bit combinations for each operating mode. */ +#define ARM7_CPSR_M_usr 0x10 +#define ARM7_CPSR_M_fiq 0x11 +#define ARM7_CPSR_M_irq 0x12 +#define ARM7_CPSR_M_svc 0x13 +#define ARM7_CPSR_M_abt 0x17 +#define ARM7_CPSR_M_und 0x11 +#define ARM7_CPSR_M_sys 0x1f + + /** Control flags for ARM7 core. */ +#define ARM7_FL_FIQ (1 << 0) +#define ARM7_FL_IRQ (1 << 1) + + /** Operating modes. */ +#define ARM7_MODE_usr 0 +#define ARM7_MODE_fiq 1 +#define ARM7_MODE_irq 2 +#define ARM7_MODE_svc 3 +#define ARM7_MODE_abt 4 +#define ARM7_MODE_und 5 +#define ARM7_MODE_sys 0 + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // register definitions + + /** ARM7 register type (all are 32-bit). */ +typedef INT32 ARM7_REG; + +enum +{ + ARM7_R0 = 0, ARM7_R1, ARM7_R2, ARM7_R3, ARM7_R4, ARM7_R5, ARM7_R6, ARM7_R7, + ARM7_R8, ARM7_R9, ARM7_R10, ARM7_R11, ARM7_R12, ARM7_R13, ARM7_R14, ARM7_R15 +}; + + /** R13 is stack pointer. */ +#define ARM7_SP 13 + /** R14 is link/return address. */ +#define ARM7_LR 14 + /** R15 is program counter. */ +#define ARM7_PC 15 + /** CPSR control register. */ +#define ARM7_CPSR 16 + /** SPSR control register. */ +#define ARM7_SPSR 17 + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** ARM7 CPU state structure. */ +struct sARM7 + { + /** All-purpose and control registers (for current mode). */ + ARM7_REG Rx [18]; + /** Banked registers for all operating modes. */ + ARM7_REG Rx_bank [6][10]; + + /** FIQ and IRQ interrupt requests. */ + int fiq, irq; + + /** Carry flag for barrel shifter and ALU operations. */ + int carry; + /** Overflow flag for arithmetic instructions. */ + int overflow; + + /** Emulation control flags. */ + int flagi; + + /** Instruction code. */ + UINT32 kod; + /** Cycle counter. */ + int cykle; + }; + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** ARM7 state. */ +extern struct sARM7 ARM7; + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // public procedures + + /** ARM7 emulator init. */ +void ARM7_Init (void); + + /** Power-ON reset. */ +void ARM7_HardReset (void); + /** Hardware reset via /RESET line. */ +void ARM7_SoftReset (void); + + /** CPSR update, possibly changing operating mode. */ +void ARM7_SetCPSR (ARM7_REG sr); + + /** Sets FIQ line state. */ +void ARM7_SetFIQ (int stan); + /** Sets IRQ line state. */ +void ARM7_SetIRQ (int stan); + + /** Tests for pending interrupts, switches to one if possible. */ +void ARM7_CheckIRQ (void); + + /** Single step. */ +void ARM7_Step (void); + /** Runs emulation for at least n cycles, returns actual amount of cycles + burned - normal interpreter. */ +int ARM7_Execute (int n); + //-------------------------------------------------------------------------- + +enum +{ + ARM7_IRQ_LINE=0, ARM7_FIRQ_LINE, + ARM7_NUM_LINES +}; + +#ifdef ENABLE_DEBUGGER +extern UINT32 arm7_disasm( char *pBuf, UINT32 pc, UINT32 opcode ); +extern UINT32 thumb_disasm( char *pBuf, UINT32 pc, UINT16 opcode ); +#endif +#endif diff --git a/plugins/ao/eng_dsf/arm7i.c b/plugins/ao/eng_dsf/arm7i.c new file mode 100644 index 00000000..cb6c7106 --- /dev/null +++ b/plugins/ao/eng_dsf/arm7i.c @@ -0,0 +1,1339 @@ +// +// ARM7 processor emulator - interpreter core +// version 1.6 / 2008-02-16 +// (c) Radoslaw Balcewicz +// + +#include "arm7.h" +#include "arm7i.h" + + //-------------------------------------------------------------------------- + // definitions and macros + + /** PC is being incremented after every instruction fetch, so we adjust for + that on all stores and jumps. */ +#define PC_ADJUSTMENT (-4) + + /** Memory access routines. */ +#include "arm7memil.c" + + /** Bit shifts compatible with IA32. */ +#define SHL(w, k) (((UINT32)(w)) << (k)) +#define SHR(w, k) (((UINT32)(w)) >> (k)) +#define SAR(w, k) (((INT32)(w)) >> (k)) +#define ROR(w, k) (SHR (w, k) | SHL (w, 32 - (k))) + + /** Byte rotation for unaligned 32-bit read. */ +#define RBOD(w, i) (ROR (w, (i) * 8)) + + /** Data processing macros. */ +#define NEG(i) ((i) & (1 << 31)) +#define POS(i) (~(i) & (1 << 31)) +#define ADDCARRY(a, b, c) \ + ((NEG (a) & NEG (b)) |\ + (NEG (a) & POS (c)) |\ + (NEG (b) & POS (c))) ? 1 : 0; +#define ADDOVERFLOW(a, b, c) \ + ((NEG (a) & NEG (b) & POS (c)) |\ + (POS (a) & POS (b) & NEG (c))) ? 1 : 0; +#define SUBCARRY(a, b, c) \ + ((NEG (a) & POS (b)) |\ + (NEG (a) & POS (c)) |\ + (POS (b) & POS (c))) ? 1 : 0; +#define SUBOVERFLOW(a, b, c)\ + ((NEG (a) & POS (b) & POS (c)) |\ + (POS (a) & NEG (b) & NEG (c))) ? 1 : 0; + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // private functions + + /** Condition EQ. */ +static int R_WEQ (void); + /** Condition NE. */ +static int R_WNE (void); + /** Condition CS. */ +static int R_WCS (void); + /** Condition CC. */ +static int R_WCC (void); + /** Condition MI. */ +static int R_WMI (void); + /** Condition PL. */ +static int R_WPL (void); + /** Condition VS. */ +static int R_WVS (void); + /** Condition VC. */ +static int R_WVC (void); + /** Condition HI. */ +static int R_WHI (void); + /** Condition LS. */ +static int R_WLS (void); + /** Condition GE. */ +static int R_WGE (void); + /** Condition LT. */ +static int R_WLT (void); + /** Condition GT. */ +static int R_WGT (void); + /** Condition LE. */ +static int R_WLE (void); + /** Condition AL. */ +static int R_WAL (void); + /** Undefined condition. */ +static int R_Wxx (void); + + /** Calculates barrel shifter output. */ +static UINT32 WyliczPrzes (void); + /** Logical shift left. */ +static UINT32 LSL_x (UINT32 w, int i); + /** Logical shift right. */ +static UINT32 LSR_x (UINT32 w, int i); + /** Arithmetic shift right. */ +static UINT32 ASR_x (UINT32 w, int i); + /** Rotate right. */ +static UINT32 ROR_x (UINT32 w, int i); + /** Rotate right extended. */ +static UINT32 RRX_1 (UINT32 w); + + /** Group 00x opcodes. */ +static void R_G00x (void); + /** Multiply instructions. */ +static void R_MUL_MLA (void); + /** Single data swap. */ +static void R_SWP (void); + /** PSR Transfer. */ +static void R_PSR (void); + /** Data processing instructions. */ +static void R_DP (void); + /** Data processing result writeback. */ +static void R_WynikDP (ARM7_REG w); + /** Data processing flags writeback. */ +static void R_FlagiDP (ARM7_REG w); + /** Single data transfer. */ +static void R_SDT (void); + /** Rozkaz "Undefined". */ +static void R_Und (); + /** Block Data Transfer. */ +static void R_BDT (); + /** Block load instructions. */ +static void R_LDM (int Rn, UINT32 adres); + /** Block store instructions. */ +static void R_STM (int Rn, UINT32 adres); + /** Branch/Branch with link. */ +static void R_B_BL (void); + /** Group 110 opcodes. */ +static void R_G110 (void); + /** Group 111 opcodes. */ +static void R_G111 (void); + +#ifdef ARM7_THUMB + /** Halfword and Signed Data Transfer. */ +static void R_HSDT (); +#endif + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // private data + + /** Flag testing functions for conditional execution. */ +static int (*s_tabWar [16]) (void) = {R_WEQ, R_WNE, R_WCS, R_WCC, R_WMI, R_WPL, + R_WVS, R_WVC, R_WHI, R_WLS, R_WGE, R_WLT, R_WGT, R_WLE, R_WAL, R_Wxx}; + /** Handler table for instruction groups. */ +static void (*s_tabGrup [8]) (void) = {R_G00x, R_G00x, R_SDT, R_SDT, R_BDT, + R_B_BL, R_G110, R_G111}; + /** Data processing instructions split to arithmetic and logical. */ +static int s_tabAL [16] = {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, + FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}; + + /** Cycles it took for current instruction to complete. */ +static int s_cykle; + //-------------------------------------------------------------------------- + + + // public functions + + + //-------------------------------------------------------------------------- + /** Single step, returns number of burned cycles. */ +int ARM7i_Step () + { + ARM7.kod = arm7_read_32 (ARM7.Rx [ARM7_PC] & ~3); + + // we increment PC here, and if there's a load from memory it will simply + // overwrite it (all PC modyfing code should be aware of this) + ARM7.Rx [ARM7_PC] += 4; + s_cykle = 2; + // condition test and group selection + if (s_tabWar [(ARM7.kod >> 28) & 15] ()) + s_tabGrup [(ARM7.kod >> 25) & 7] (); + return s_cykle; + } + //-------------------------------------------------------------------------- + + + // private functions + + + //-------------------------------------------------------------------------- + /** Condition EQ. */ +int R_WEQ () + { + // "Z set" + return ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z; + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition NE. */ +int R_WNE () + { + // "Z clear" + return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition CS. */ +int R_WCS () + { + // "C set" + return ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C; + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition CC. */ +int R_WCC () + { + // "C clear" + return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition MI. */ +int R_WMI () + { + // "N set" + return ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N; + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition PL. */ +int R_WPL () + { + // "N clear" + return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition VS. */ +int R_WVS () + { + // "V set" + return ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V; + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition VC. */ +int R_WVC () + { + // "V clear" + return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition HI. */ +int R_WHI () + { + // "C set and Z clear" + return (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) &&\ + !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition LS. */ +int R_WLS () + { + // "C clear or Z set" + return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ||\ + (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition GE. */ +int R_WGE () + { + // "N equals V" + return (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N) &&\ + (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V) || !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N) &&\ + !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition LT. */ +int R_WLT () + { + // "N not equal to V" + return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N) &&\ + (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V) || (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N) &&\ + !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition GT. */ +int R_WGT () + { + // "Z clear AND (N equals V)" + return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z) && R_WGE (); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition LE. */ +int R_WLE () + { + // "Z set OR (N not equal to V)" + return (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z) || R_WLT (); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Condition AL. */ +int R_WAL () + { + // "(ignored)" + return TRUE; + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Undefined condition. */ +int R_Wxx () + { + // behaviour undefined + return FALSE; + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Calculates barrel shifter output. */ +UINT32 WyliczPrzes () + { + int Rm, Rs, i; + UINT32 w; + + // Rm is source for the shift operation + Rm = ARM7.kod & 15; + + if (ARM7.kod & (1 << 4)) + { + s_cykle++; + // shift count in Rs (8 lowest bits) + if (Rm != ARM7_PC) + w = ARM7.Rx [Rm]; + else + w = (ARM7.Rx [ARM7_PC] & ~3) + 12 + PC_ADJUSTMENT; + // Rs can't be PC + Rs = (ARM7.kod >> 8) & 15; + i = (UINT8)ARM7.Rx [Rs]; + if (i == 0) + { + // special case + ARM7.carry = (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 1 : 0; + return w; + } + + switch ((ARM7.kod >> 5) & 3) + { + case 0: + w = LSL_x (w, i); + break; + case 1: + w = LSR_x (w, i); + break; + case 2: + w = ASR_x (w, i); + break; + case 3: + w = ROR_x (w, i); + break; + } + } + else + { + // shift count as immediate in opcode + if (Rm != ARM7_PC) + w = ARM7.Rx [Rm]; + else + w = (ARM7.Rx [ARM7_PC] & ~3) + 8 + PC_ADJUSTMENT; + i = (ARM7.kod >> 7) & 31; + + switch ((ARM7.kod >> 5) & 3) + { + case 0: + w = LSL_x (w, i); + break; + case 1: + if (i > 0) + w = LSR_x (w, i); + else + w = LSR_x (w, 32); + break; + case 2: + if (i > 0) + w = ASR_x (w, i); + else + w = ASR_x (w, 32); + break; + case 3: + if (i > 0) + w = ROR_x (w, i); + else + w = RRX_1 (w); + break; + } + } + return w; + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Logical shift left. */ +UINT32 LSL_x (UINT32 w, int i) +{ + // LSL #0 copies C into carry out and returns unmodified value + if (i == 0) + { + ARM7.carry = (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 1 : 0; + return w; + } + // LSL #32 copies LSB to carry out and returns zero + if (i == 32) + { + ARM7.carry = w & 1; + return 0; + } + // LSL > #32 returns zero for both carry and output + if (i > 32) + { + ARM7.carry = 0; + return 0; + } + // normal shift + ARM7.carry = (w & (1 << (32 - i))) ? 1 : 0; + w = SHL (w, i); + return w; +} + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Logical shift right. */ +UINT32 LSR_x (UINT32 w, int i) +{ + // LSR #32 copies MSB to carry out and returns zero + if (i == 32) + { + ARM7.carry = (w & (1 << 31)) ? 1 : 0; + return 0; + } + // LSR > #32 returns zero for both carry and output + if (i > 32) + { + ARM7.carry = 0; + return 0; + } + // normal shift + ARM7.carry = (w & (1 << (i - 1))) ? 1 : 0; + w = SHR (w, i); + return w; +} + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Arithmetic shift right. */ +UINT32 ASR_x (UINT32 w, int i) +{ + // ASR >= #32 carry out and output value depends on the minus sign + if (i >= 32) + { + if (w & (1 << 31)) + { + ARM7.carry = 1; + return ~0; + } + + ARM7.carry = 0; + return 0; + } + // normal shift + ARM7.carry = (w & (1 << (i - 1))) ? 1 : 0; + w = SAR (w, i); + return w; +} + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Rotate right. */ +UINT32 ROR_x (UINT32 w, int i) +{ + // mask count to [0; 31] + i &= 0x1f; + // ROR #32,#64,etc. copies MSB into carry out and returns unmodified value + if (i == 0) + { + ARM7.carry = (w & (1 << 31)) ? 1 : 0; + return w; + } + // normal shift + ARM7.carry = (w & (1 << (i-1))) ? 1 : 0; + w = ROR (w, i); + return w; +} + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Rotate right extended. */ +UINT32 RRX_1 (UINT32 w) + { + // same as RCR by 1 in IA32 + ARM7.carry = w & 1; + return (w >> 1) | ((ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) << 2); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Group 00x opcodes. */ +void R_G00x () + { +#ifdef ARM7_THUMB + // 24 constant bits + if ((ARM7.kod & 0x0ffffff0) == 0x012fff10) // BX - branch with possible mode transfer + { + #ifdef ARM7_THUMB + int Rn = ARM7.Rx[ARM7.kod & 0xf]; + + // switching to Thumb mode? + if (Rn & 1) + { + ARM7_SetCPSR(ARM7.Rx[ARM7_CPSR] | ARM7_CPSR_T); + } + + ARM7.Rx[ARM7_PC] = Rn & ~1; + #endif + } + // 15 constant bits + else if ((ARM7.kod & 0x0fb00ff0) == 0x01000090) + R_SWP (); + // 10 constant bits + else if ((ARM7.kod & 0x0fc000f0) == 0x00000090) + R_MUL_MLA (); + // 10 constant bits + else if ((ARM7.kod & 0x0e400f90) == 0x00000090) + R_HSDT (); + // 9 constant bits + else if ((ARM7.kod & 0x0f8000f0) == 0x00800090) + { +// logerror("G00x / Multiply long\n"); + } + // 6 constant bits + else if ((ARM7.kod & 0x0e400090) == 0x00400090) + R_HSDT (); + // 2 constant bits + else + { + if ((ARM7.kod & 0x01900000) == 0x01000000) + // TST, TEQ, CMP & CMN without S bit are "PSR Transfer" + R_PSR (); + else + // the rest is "Data processing" + R_DP (); + } +#else + if ((ARM7.kod & 0x03b00090) == 0x01000090) + R_SWP (); + else if ((ARM7.kod & 0x03c00090) == 0x00000090) + R_MUL_MLA (); + else + { + if ((ARM7.kod & 0x01900000) == 0x01000000) + // TST, TEQ, CMP & CMN without S bit are "PSR Transfer" + R_PSR (); + else + // the rest is "Data processing" + R_DP (); + } +#endif + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Single data swap. */ +void R_SWP () + { + int Rn, Rd, Rm; + UINT32 adres, w; + +#define BIT_B (ARM7.kod & (1 << 21)) + + s_cykle += 4; + // none of these can be PC + Rn = (ARM7.kod >> 16) & 15; + Rd = (ARM7.kod >> 12) & 15; + Rm = ARM7.kod & 15; + adres = ARM7.Rx [Rn]; + + if (BIT_B) + { + // "byte" + w = arm7_read_8 (adres); + arm7_write_8 (adres, (UINT8)ARM7.Rx [Rm]); + } + else + { + // "word" + w = RBOD (arm7_read_32 (adres & ~3), adres & 3); + arm7_write_32 (adres & ~3, ARM7.Rx [Rm]); + } + ARM7.Rx [Rd] = w; + +#undef BIT_B + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Multiply instructions. */ +void R_MUL_MLA () + { + int Rm, Rs, Rn, Rd; + UINT32 wynik; + +#define BIT_A (ARM7.kod & (1 << 21)) +#define BIT_S (ARM7.kod & (1 << 20)) + + s_cykle += 2; + // none of these can be PC, also Rd != Rm + Rd = (ARM7.kod >> 16) & 15, + Rs = (ARM7.kod >> 8) & 15, + Rm = ARM7.kod & 15; + + // MUL + wynik = ARM7.Rx [Rm] * ARM7.Rx [Rs]; + if (BIT_A) + { + // MLA + Rn = (ARM7.kod >> 12) & 15; + wynik += ARM7.Rx [Rn]; + } + ARM7.Rx [Rd] = wynik; + + if (BIT_S) + { + // V remains unchanged, C is undefined + ARM7.Rx [ARM7_CPSR] &= ~(ARM7_CPSR_N | ARM7_CPSR_Z); + if (wynik == 0) + ARM7.Rx [ARM7_CPSR] |= ARM7_CPSR_Z; + ARM7.Rx [ARM7_CPSR] |= wynik & 0x80000000; + } + +#undef BIT_S +#undef BIT_A + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** PSR Transfer. */ +void R_PSR () + { + int Rd, Rm; + UINT32 w, arg; + +#define BIT_I (ARM7.kod & (1 << 25)) +#define BIT_P (ARM7.kod & (1 << 22)) + + // none of the registers involved can be PC + + if (ARM7.kod & (1 << 21)) + { + // MSR + Rm = ARM7.kod & 15; + if (BIT_I) + // immediate (lower 12 bits) + arg = ROR (ARM7.kod & 0xff, ((ARM7.kod >> 8) & 0xf) * 2); + else + // register + arg = ARM7.Rx [Rm]; + + // decode mask bits + if (BIT_P) + { + w = ARM7.Rx [ARM7_SPSR]; + if (ARM7_CPSR_M (ARM7.Rx [ARM7_CPSR]) > ARM7_CPSR_M_usr &&\ + ARM7_CPSR_M (ARM7.Rx [ARM7_CPSR]) < ARM7_CPSR_M_sys) + { + if (ARM7.kod & (1 << 16)) + w = (w & 0xffffff00) | (arg & 0x000000ff); + if (ARM7.kod & (1 << 17)) + w = (w & 0xffff00ff) | (arg & 0x0000ff00); + if (ARM7.kod & (1 << 18)) + w = (w & 0xff00ffff) | (arg & 0x00ff0000); + if (ARM7.kod & (1 << 19)) + // ARMv5E should have 0xf8000000 argument mask + w = (w & 0x00ffffff) | (arg & 0xf0000000); + } + // force valid mode + w |= 0x10; + ARM7.Rx [ARM7_SPSR] = w; + } + else + { + w = ARM7.Rx [ARM7_CPSR]; + // only flags can be changed in User mode + if (ARM7_CPSR_M (ARM7.Rx [ARM7_CPSR]) != ARM7_CPSR_M_usr) + { + if (ARM7.kod & (1 << 16)) + w = (w & 0xffffff00) | (arg & 0x000000ff); + if (ARM7.kod & (1 << 17)) + w = (w & 0xffff00ff) | (arg & 0x0000ff00); + if (ARM7.kod & (1 << 18)) + w = (w & 0xff00ffff) | (arg & 0x00ff0000); + } + if (ARM7.kod & (1 << 19)) + // ARMv5E should have 0xf8000000 argument mask + w = (w & 0x00ffffff) | (arg & 0xf0000000); + // force valid mode + w |= 0x10; + ARM7_SetCPSR (w); + } + } + else + { + // MRS + Rd = (ARM7.kod >> 12) & 15; + if (BIT_P) + ARM7.Rx [Rd] = ARM7.Rx [ARM7_SPSR]; + else + ARM7.Rx [Rd] = ARM7.Rx [ARM7_CPSR]; + } + +#undef BIT_P +#undef BIT_I + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Data processing instructions. */ +void R_DP () + { + int Rn; + ARM7_REG arg1, arg2, w; + +#define BIT_I (ARM7.kod & (1 << 25)) + + // Rn can be PC, so we need to account for that + Rn = (ARM7.kod >> 16) & 15; + + if (BIT_I) + { + if (Rn != ARM7_PC) + arg1 = ARM7.Rx [Rn]; + else + arg1 = (ARM7.Rx [ARM7_PC] & ~3) + 8 + PC_ADJUSTMENT; + // immediate in lowest 12 bits + arg2 = ROR (ARM7.kod & 0xff, ((ARM7.kod >> 8) & 0xf) * 2); + // preload carry out from C + ARM7.carry = (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 1 : 0; + } + else + { + if (Rn != ARM7_PC) + arg1 = ARM7.Rx [Rn]; + else + // register or immediate shift? + if (ARM7.kod & (1 << 4)) + arg1 = (ARM7.Rx [ARM7_PC] & ~3) + 12 + PC_ADJUSTMENT; + else + arg1 = (ARM7.Rx [ARM7_PC] & ~3) + 8 + PC_ADJUSTMENT; + // calculate in barrel shifter + arg2 = WyliczPrzes (); + } + + // decode instruction type + switch ((ARM7.kod >> 21) & 15) + { + case 0: + // AND + R_WynikDP (arg1 & arg2); + break; + + case 1: + // EOR + R_WynikDP (arg1 ^ arg2); + break; + + case 2: + // SUB + w = arg1 - arg2; + ARM7.carry = SUBCARRY (arg1, arg2, w); + ARM7.overflow = SUBOVERFLOW (arg1, arg2, w); + R_WynikDP (w); + break; + + case 3: + // RSB + w = arg2 - arg1; + ARM7.carry = SUBCARRY (arg2, arg1, w); + ARM7.overflow = SUBOVERFLOW (arg2, arg1, w); + R_WynikDP (w); + break; + + case 4: + // ADD + w = arg1 + arg2; + ARM7.carry = ADDCARRY (arg1, arg2, w); + ARM7.overflow = ADDOVERFLOW (arg1, arg2, w); + R_WynikDP (w); + break; + + case 5: + // ADC + w = arg1 + arg2 + ((ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 1 : 0); + ARM7.carry = ADDCARRY (arg1, arg2, w); + ARM7.overflow = ADDOVERFLOW (arg1, arg2, w); + R_WynikDP (w); + break; + + case 6: + // SBC + w = arg1 - arg2 - ((ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 0 : 1); + ARM7.carry = SUBCARRY (arg1, arg2, w); + ARM7.overflow = SUBOVERFLOW (arg1, arg2, w); + R_WynikDP (w); + break; + + case 7: + // RSC + w = arg2 - arg1 - ((ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 0 : 1); + ARM7.carry = SUBCARRY (arg2, arg1, w); + ARM7.overflow = SUBOVERFLOW (arg2, arg1, w); + R_WynikDP (w); + break; + + case 8: + // TST + R_FlagiDP (arg1 & arg2); + break; + + case 9: + // TEQ + R_FlagiDP (arg1 ^ arg2); + break; + + case 10: + // CMP + w = arg1 - arg2; + ARM7.carry = SUBCARRY (arg1, arg2, w); + ARM7.overflow = SUBOVERFLOW (arg1, arg2, w); + R_FlagiDP (w); + break; + + case 11: + // CMN + w = arg1 + arg2; + ARM7.carry = ADDCARRY (arg1, arg2, w); + ARM7.overflow = ADDOVERFLOW (arg1, arg2, w); + R_FlagiDP (w); + break; + + case 12: + // ORR + R_WynikDP (arg1 | arg2); + break; + + case 13: + // MOV + R_WynikDP (arg2); + break; + + case 14: + // BIC + R_WynikDP (arg1 & ~arg2); + break; + + case 15: + // MVN + R_WynikDP (~arg2); + break; + } + +#undef BIT_I + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Data processing result writeback. */ +void R_WynikDP (ARM7_REG w) + { + int Rd; + +#define BIT_S (ARM7.kod & (1 << 20)) + + Rd = (ARM7.kod >> 12) & 15; + ARM7.Rx [Rd] = w; + if (BIT_S) + { + if (Rd == ARM7_PC) + { + s_cykle += 4; + // copy current SPSR to CPSR + ARM7_SetCPSR (ARM7.Rx [ARM7_SPSR]); + } + else + // save new flags + R_FlagiDP (w); + } + +#undef BIT_S + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Data processing flags writeback. */ +void R_FlagiDP (ARM7_REG w) + { + // arithmetic or logical instruction? + if (s_tabAL [(ARM7.kod >> 21) & 15]) + { + ARM7.Rx [ARM7_CPSR] &= ~(ARM7_CPSR_N | ARM7_CPSR_Z | ARM7_CPSR_C |\ + ARM7_CPSR_V); + ARM7.Rx [ARM7_CPSR] |= ARM7.overflow << 28; + } + else + ARM7.Rx [ARM7_CPSR] &= ~(ARM7_CPSR_N | ARM7_CPSR_Z | ARM7_CPSR_C); + ARM7.Rx [ARM7_CPSR] |= ARM7.carry << 29; + if (w == 0) + ARM7.Rx [ARM7_CPSR] |= ARM7_CPSR_Z; + ARM7.Rx [ARM7_CPSR] |= w & 0x80000000; + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Single data transfer. */ +void R_SDT (void) + { + int Rn, Rd, offset; + UINT32 adres, w = 0; + +#define BIT_I (ARM7.kod & (1 << 25)) +#define BIT_P (ARM7.kod & (1 << 24)) +#define BIT_U (ARM7.kod & (1 << 23)) +#define BIT_B (ARM7.kod & (1 << 22)) +#define BIT_W (ARM7.kod & (1 << 21)) +#define BIT_L (ARM7.kod & (1 << 20)) + + if (BIT_I && (ARM7.kod & (1 << 4))) + { + R_Und (); + return; + } + + Rn = (ARM7.kod >> 16) & 15, + Rd = (ARM7.kod >> 12) & 15; + if (Rn != ARM7_PC) + adres = ARM7.Rx [Rn]; + else + adres = ARM7.Rx [ARM7_PC] & ~3; + if (!BIT_L) + if (Rd != ARM7_PC) + w = ARM7.Rx [Rd]; + else + w = (ARM7.Rx [ARM7_PC] & ~3) + 12 + PC_ADJUSTMENT; + + if (BIT_I) + // calculate value in barrel shifter + offset = WyliczPrzes (); + else + // immediate in lowest 12 bits + offset = ARM7.kod & 0xfff; + + if (!BIT_U) + offset = -offset; + if (BIT_P) + { + // "pre-index" + adres += offset; + if (BIT_W) + // "write-back" + ARM7.Rx [Rn] = adres; + } + else + // "post-index" + ARM7.Rx [Rn] += offset; + if (Rn == ARM7_PC) + adres += 8 + PC_ADJUSTMENT; + + if (BIT_L) + { + s_cykle += 3; + // "load" + if (BIT_B) + // "byte" + ARM7.Rx [Rd] = arm7_read_8 (adres); + else + // "word" + ARM7.Rx [Rd] = RBOD (arm7_read_32 (adres & ~3), adres & 3); + } + else + { + s_cykle += 2; + // "store" + if (BIT_B) + // "byte" + arm7_write_8 (adres, (UINT8)w); + else + // "word" + arm7_write_32 (adres & ~3, w); + } + +#undef BIT_L +#undef BIT_W +#undef BIT_B +#undef BIT_U +#undef BIT_P +#undef BIT_I + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Undefined. */ +void R_Und () + { + UINT32 sr = ARM7.Rx [ARM7_CPSR]; + ARM7_SetCPSR (ARM7_CPSR_MX (sr, ARM7_CPSR_M_und) | ARM7_CPSR_I); + ARM7.Rx [ARM7_SPSR] = sr; + ARM7.Rx [ARM7_LR] = ARM7.Rx [ARM7_PC] + 4; + ARM7.Rx [ARM7_PC] = 0x00000004; + } + //-------------------------------------------------------------------------- + +#define BIT_U (ARM7.kod & (1 << 23)) +#define BIT_S (ARM7.kod & (1 << 22)) + //-------------------------------------------------------------------------- + /** Block Data Transfer. */ +void R_BDT () + { + int Rn, usr = FALSE; + UINT32 adres; + ARM7_REG cpsr = 0; + +#define BIT_L (ARM7.kod & (1 << 20)) + + // Rn can't be PC + Rn = (ARM7.kod >> 16) & 15; + adres = ARM7.Rx [Rn]; + + // transfer in User mode + if (BIT_S) + if (!BIT_L || !(ARM7.kod & (1 << ARM7_PC))) + usr = TRUE; + + if (usr) + { +//EMU_BLAD (BLAD_WEWNETRZNY, "BDT: user transfer"); + cpsr = ARM7.Rx [ARM7_CPSR]; + ARM7_SetCPSR (ARM7_CPSR_MX (cpsr, ARM7_CPSR_M_usr)); + } + + if (BIT_L) + // "load" + R_LDM (Rn, adres); + else + // "store" + R_STM (Rn, adres); + + if (usr) + ARM7_SetCPSR (cpsr); + +#undef BIT_L + } + //-------------------------------------------------------------------------- + +#define BIT_P (ARM7.kod & (1 << 24)) +#define BIT_W (ARM7.kod & (1 << 21)) + //-------------------------------------------------------------------------- + /** Block load instructions. */ +void R_LDM (int Rn, UINT32 adres) + { + int i, n, sp; + + // count registers on the list + for (i = 0, n = 0; i < 16; i++) + if (ARM7.kod & (1 << i)) + n++; + s_cykle += n * 2 + 1; + + n <<= 2; + // transfer type + sp = BIT_P; + if (!BIT_U) + { + // "down" + n = -n; + adres += n; + sp = !sp; + } + if (BIT_W) + // "write-back" + ARM7.Rx [Rn] += n; + + // for all registers in mask + if (sp) + for (i = 0; i < 16; i++) + { + if (!(ARM7.kod & (1 << i))) + continue; + adres += 4; + ARM7.Rx [i] = arm7_read_32 (adres); + } + else + for (i = 0; i < 16; i++) + { + if (!(ARM7.kod & (1 << i))) + continue; + ARM7.Rx [i] = arm7_read_32 (adres); + adres += 4; + } + + // special case - mode change when PC is written + if ((ARM7.kod & (1 << ARM7_PC)) && BIT_S) + ARM7_SetCPSR (ARM7.Rx [ARM7_SPSR]); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Block store instructions. */ +void R_STM (int Rn, UINT32 adres) + { + int i, n, p, sp; + + // count registers on the list and remember the first one + for (i = 0, n = 0, p = -1; i < 16; i++) + if (ARM7.kod & (1 << i)) + { + n++; + if (p < 0) + p = i; + } + s_cykle += n * 2; + + n <<= 2; + // transfer type + sp = BIT_P; + if (!BIT_U) + { + // "down" + n = -n; + adres += n; + sp = !sp; + } + // if base register is not the first one to transfer, writeback happens here + if (BIT_W && Rn != p) + // "write-back" + ARM7.Rx [Rn] += n; + + // registers R0-R14 + if (sp) + for (i = 0; i < 15; i++) + { + if (!(ARM7.kod & (1 << i))) + continue; + adres += 4; + arm7_write_32 (adres, ARM7.Rx [i]); + } + else + for (i = 0; i < 15; i++) + { + if (!(ARM7.kod & (1 << i))) + continue; + arm7_write_32 (adres, ARM7.Rx [i]); + adres += 4; + } + + // PC is a special case + if (ARM7.kod & (1 << ARM7_PC)) + { + if (sp) + { + adres += 4; + arm7_write_32 (adres, (ARM7.Rx [ARM7_PC] & ~3) + 12 + PC_ADJUSTMENT); + } + else + { + arm7_write_32 (adres, (ARM7.Rx [ARM7_PC] & ~3) + 12 + PC_ADJUSTMENT); + adres += 4; + } + } + + // if base register is the first one to transfer, writeback happens here + if (BIT_W && Rn == p) + // "write-back" + ARM7.Rx [Rn] += n; + } + //-------------------------------------------------------------------------- +#undef BIT_W +#undef BIT_P +#undef BIT_S +#undef BIT_U + + //-------------------------------------------------------------------------- + /** Branch/Branch with link. */ +void R_B_BL () + { + INT32 offset; + +#define BIT_L (ARM7.kod & (1 << 24)) + + s_cykle += 4; + offset = (ARM7.kod & 0x00ffffff) << 2; + if (offset & 0x02000000) + offset |= 0xfc000000; + offset += 8 + PC_ADJUSTMENT; + if (BIT_L) + // "Branch with link" + ARM7.Rx [ARM7_LR] = (ARM7.Rx [ARM7_PC] & ~3) + 4 + PC_ADJUSTMENT; + // "Branch" + ARM7.Rx [ARM7_PC] += offset; + +#undef BIT_L + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Group 110 opcodes. */ +void R_G110 () + { +// logerror("ARM7: G110 / Coprocessor data transfer\n"); + } + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + /** Group 111 opcodes. */ +void R_G111 () + { + if ((ARM7.kod & 0xf0000000) == 0xe0000000) + { +/* if (ARM7.kod & (1 << 4)) + logerror("ARM7: G111 / Coprocessor register transfer\n"); + else + logerror("ARM7: G111 / Coprocessor data operation\n"); */ + } + else + { + UINT32 sr = ARM7.Rx [ARM7_CPSR]; + ARM7_SetCPSR (ARM7_CPSR_MX (sr, ARM7_CPSR_M_svc) | ARM7_CPSR_I); + ARM7.Rx [ARM7_SPSR] = sr; + ARM7.Rx [ARM7_LR] = ARM7.Rx [ARM7_PC]; + ARM7.Rx [ARM7_PC] = 0x00000008; + } + } + //-------------------------------------------------------------------------- + +#ifdef ARM7_THUMB + //-------------------------------------------------------------------------- + /** Halfword and Signed Data Transfer. */ +void R_HSDT () + { + int Rm, Rd, Rn, offset; + uint32_t adres, w; + +#define BIT_P (ARM7.kod & (1 << 24)) +#define BIT_U (ARM7.kod & (1 << 23)) +#define BIT_W (ARM7.kod & (1 << 21)) +#define BIT_L (ARM7.kod & (1 << 20)) +#define BIT_S (ARM7.kod & (1 << 6)) +#define BIT_H (ARM7.kod & (1 << 5)) + + // Rm can't be PC + Rn = (ARM7.kod >> 16) & 15; + Rd = (ARM7.kod >> 12) & 15; + if (Rn != ARM7_PC) + adres = ARM7.Rx [Rn]; + else + adres = ARM7.Rx [ARM7_PC] & ~3; + if (!BIT_L) + if (Rd != ARM7_PC) + w = ARM7.Rx [Rd]; + else + w = (ARM7.Rx [ARM7_PC] & ~3) + 12 + POPRAWKA_PC; + + if (1 << 22) + // immediate + offset = ((ARM7.kod >> 4) & 0xf0) | (ARM7.kod & 15); + else + { + // register + Rm = ARM7.kod & 15; + offset = ARM7.Rx [Rm]; + } + + if (!BIT_U) + offset = -offset; + if (BIT_P) + { + // "pre-index" + adres += offset; + if (BIT_W) + // "write-back" + ARM7.Rx [Rn] = adres; + } + else + // "post-index" + ARM7.Rx [Rn] += offset; + if (Rn == ARM7_PC) + adres += 8 + POPRAWKA_PC; + + if (BIT_L) + { + // "load" + s_cykle += 3; + if (BIT_S) + { + if (BIT_H) + // "signed halfword" + ARM7.Rx [Rd] = (INT32)(INT16)arm7_read_16 (adres); + else + // "signed byte" + ARM7.Rx [Rd] = (INT32)(INT8)arm7_read_8 (adres); + } + else + // "unsigned halfword" + ARM7.Rx [Rd] = arm7_read_16 (adres); + } + else + { + // store + s_cykle += 2; + if (BIT_H) + // "halfword" + arm7_write_16 (adres, (UINT16)w); + else + // "byte" + arm7_write_8 (adres, (UINT8)w); + } + +#undef BIT_H +#undef BIT_S +#undef BIT_L +#undef BIT_W +#undef BIT_U +#undef BIT_P + } + //-------------------------------------------------------------------------- +#endif diff --git a/plugins/ao/eng_dsf/arm7i.h b/plugins/ao/eng_dsf/arm7i.h new file mode 100644 index 00000000..5a2e611b --- /dev/null +++ b/plugins/ao/eng_dsf/arm7i.h @@ -0,0 +1,19 @@ +// +// ARM7 processor emulator - interpreter core +// version 1.6 / 2008-02-16 +// (c) Radoslaw Balcewicz +// + +#ifndef _ARM7i_h_ +#define _ARM7i_h_ + +#include "arm7.h" + + //-------------------------------------------------------------------------- + // public functions + + /** Single step, returns number of burned cycles. */ +int ARM7i_Step(void); + //-------------------------------------------------------------------------- + +#endif diff --git a/plugins/ao/eng_dsf/arm7memil.c b/plugins/ao/eng_dsf/arm7memil.c new file mode 100644 index 00000000..b9daed83 --- /dev/null +++ b/plugins/ao/eng_dsf/arm7memil.c @@ -0,0 +1,56 @@ +// memory inline functions shared by ARM and THUMB modes + +static INLINE void arm7_write_32(UINT32 addr, UINT32 data ) +{ + addr &= ~3; + dc_write32(addr,data); +} + + +static INLINE void arm7_write_16(UINT32 addr, UINT16 data) +{ + addr &= ~1; + dc_write16(addr,data); +} + +static INLINE void arm7_write_8(UINT32 addr, UINT8 data) +{ + dc_write8(addr,data); +} + +static INLINE UINT32 arm7_read_32(UINT32 addr) +{ + UINT32 result; + int k = (addr & 3) << 3; + + if (k) + { + result = dc_read32 (addr & ~3); + result = (result >> k) | (result << (32 - k)); + } + else + { + result = dc_read32(addr); + } + return result; +} + +static INLINE UINT16 arm7_read_16(UINT32 addr) +{ + UINT16 result; + + result = dc_read16(addr & ~1); + + if (addr & 1) + { + result = ((result>>8) & 0xff) | ((result&0xff)<<8); + } + + return result; +} + +static INLINE UINT8 arm7_read_8(UINT32 addr) +{ + return dc_read8(addr); +} + diff --git a/plugins/ao/eng_dsf/arm7thumb.c b/plugins/ao/eng_dsf/arm7thumb.c new file mode 100644 index 00000000..f9780cab --- /dev/null +++ b/plugins/ao/eng_dsf/arm7thumb.c @@ -0,0 +1,1176 @@ +// +// ARM7 processor emulator - THUMB support +// version 2.0 / 2008-02-08 +// By R. Belmont and SGINut +// + +#include "arm7.h" +#include "arm7i.h" +#include "arm7thumb.h" + +// base cycle counts for Thumb instructions +static const int thumbCycles[256] = +{ +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 4 + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 5 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 6 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 7 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 8 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 9 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // a + 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1, // b + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, // d + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // e + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 // f +}; + +// utility macros + +#define IsNeg(i) ((i) >> 31) +#define IsPos(i) ((~(i)) >> 31) + +#define N_BIT 31 +#define Z_BIT 30 +#define C_BIT 29 +#define V_BIT 28 +#define SIGN_BIT ((UINT32)(1<<31)) + +#define HandleThumbALUAddFlags(rd, rn, op2) \ + ARM7_SetCPSR( \ + ((GET_CPSR &~ (ARM7_CPSR_N | ARM7_CPSR_Z | ARM7_CPSR_V | ARM7_CPSR_C)) \ + | (((!THUMB_SIGN_BITS_DIFFER(rn, op2)) && THUMB_SIGN_BITS_DIFFER(rn, rd)) \ + << V_BIT) \ + | (((~(rn)) < (op2)) << C_BIT) \ + | HandleALUNZFlags(rd))); \ + R15 += 2; + +#define HandleThumbALUSubFlags(rd, rn, op2) \ + ARM7_SetCPSR( \ + ((GET_CPSR &~ (ARM7_CPSR_N | ARM7_CPSR_Z | ARM7_CPSR_V | ARM7_CPSR_C)) \ + | ((THUMB_SIGN_BITS_DIFFER(rn, op2) && THUMB_SIGN_BITS_DIFFER(rn, rd)) \ + << V_BIT) \ + | (((IsNeg(rn) & IsPos(op2)) | (IsNeg(rn) & IsPos(rd)) | (IsPos(op2) & IsPos(rd))) ? ARM7_CPSR_C : 0) \ + | HandleALUNZFlags(rd))); \ + R15 += 2; + +#define HandleALUNZFlags(rd) \ + (((rd) & SIGN_BIT) | ((!(rd)) << Z_BIT)) + +// memory accessors +#include "arm7memil.c" + +// public functions +int ARM7i_Thumb_Step() +{ + UINT32 readword; + UINT32 addr, insn; + UINT32 rm, rn, rs, rd, op2, imm, rrs, rrd; + INT32 offs; + UINT32 pc = ARM7.Rx[ARM7_PC]; + int cycles; + + insn = arm7_read_16(pc & (~1)); + + cycles = (3 - thumbCycles[insn >> 8]); + + switch( ( insn & THUMB_INSN_TYPE ) >> THUMB_INSN_TYPE_SHIFT ) + { + case 0x0: /* Logical shifting */ + ARM7_SetCPSR(GET_CPSR &~ (ARM7_CPSR_N | ARM7_CPSR_Z)); + if( insn & THUMB_SHIFT_R ) /* Shift right */ + { + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rrs = GET_REGISTER(rs); + offs = ( insn & THUMB_SHIFT_AMT ) >> THUMB_SHIFT_AMT_SHIFT; + if( offs != 0 ) + { + SET_REGISTER( rd, rrs >> offs ); + if( rrs & ( 1 << (offs-1) ) ) + { + ARM7_SetCPSR(GET_CPSR | ARM7_CPSR_C); + } + else + { + ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_C); + } + } + else + { + SET_REGISTER( rd, 0 ); + if( rrs & 0x80000000 ) + { + ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C ); + } + else + { + ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C ); + } + } + ARM7_SetCPSR(GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + } + else /* Shift left */ + { + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rrs = GET_REGISTER(rs); + offs = ( insn & THUMB_SHIFT_AMT ) >> THUMB_SHIFT_AMT_SHIFT; + if( offs != 0 ) + { + SET_REGISTER( rd, rrs << offs ); + if( rrs & ( 1 << ( 31 - ( offs - 1 ) ) ) ) + { + ARM7_SetCPSR(GET_CPSR | ARM7_CPSR_C); + } + else + { + ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_C); + } + } + else + { + SET_REGISTER( rd, rrs ); + } + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + } + break; + case 0x1: /* Arithmetic */ + if( insn & THUMB_INSN_ADDSUB ) + { + switch( ( insn & THUMB_ADDSUB_TYPE ) >> THUMB_ADDSUB_TYPE_SHIFT ) + { + case 0x0: /* ADD Rd, Rs, Rn */ + rn = GET_REGISTER( ( insn & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT ); + rs = GET_REGISTER( ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT ); + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + SET_REGISTER( rd, rs + rn ); + HandleThumbALUAddFlags( GET_REGISTER(rd), rs, rn ); + break; + case 0x1: /* SUB Rd, Rs, Rn */ + rn = GET_REGISTER( ( insn & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT ); + rs = GET_REGISTER( ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT ); + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + SET_REGISTER( rd, rs - rn ); + HandleThumbALUSubFlags( GET_REGISTER(rd), rs, rn ); + break; + case 0x2: /* ADD Rd, Rs, #imm */ + imm = ( insn & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT; + rs = GET_REGISTER( ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT ); + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + SET_REGISTER( rd, rs + imm ); + HandleThumbALUAddFlags( GET_REGISTER(rd), rs, imm ); + break; + case 0x3: /* SUB Rd, Rs, #imm */ + imm = ( insn & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT; + rs = GET_REGISTER( ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT ); + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + SET_REGISTER( rd, rs - imm ); + HandleThumbALUSubFlags( GET_REGISTER(rd), rs,imm ); + break; + default: + printf("%08x: G1 Undefined Thumb instruction: %04x\n", pc, insn); + R15 += 2; + break; + } + } + else + { + /* ASR.. */ + //if( insn & THUMB_SHIFT_R ) /* Shift right */ + { + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rrs = GET_REGISTER(rs); + offs = ( insn & THUMB_SHIFT_AMT ) >> THUMB_SHIFT_AMT_SHIFT; + if( offs == 0 ) + { + offs = 32; + } + + if( offs >= 32 ) + { + if( rrs >> 31 ) + { + ARM7_SetCPSR(GET_CPSR | ARM7_CPSR_C); + } + else + { + ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_C); + } + SET_REGISTER( rd, ( rrs & 0x80000000 ) ? 0xFFFFFFFF : 0x00000000 ); + } + else + { + if( ( rrs >> ( offs - 1 ) ) & 1 ) + { + ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C ); + } + else + { + ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C ); + } + SET_REGISTER( rd, ( rrs & 0x80000000 ) ? ( ( 0xFFFFFFFF << ( 32 - offs ) ) | ( rrs >> offs ) ) : ( rrs >> offs ) ); + } + ARM7_SetCPSR(GET_CPSR &~ (ARM7_CPSR_N | ARM7_CPSR_Z)); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + } + + } + break; + case 0x2: /* CMP / MOV */ + if( insn & THUMB_INSN_CMP ) + { + rn = GET_REGISTER( ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT ); + op2 = ( insn & THUMB_INSN_IMM ); + rd = rn - op2; + HandleThumbALUSubFlags( rd, rn, op2 ); + //mame_printf_debug("%08x: xxx Thumb instruction: CMP R%d (%08x), %02x (N=%d, Z=%d, C=%d, V=%d)\n", pc, ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT, GET_REGISTER( ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT ), op2, N_IS_SET(GET_CPSR) ? 1 : 0, Z_IS_SET(GET_CPSR) ? 1 : 0, C_IS_SET(GET_CPSR) ? 1 : 0, V_IS_SET(GET_CPSR) ? 1 : 0); + } + else + { + rd = ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT; + op2 = ( insn & THUMB_INSN_IMM ); + SET_REGISTER( rd, op2 ); + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + } + break; + case 0x3: /* ADD/SUB immediate */ + if( insn & THUMB_INSN_SUB ) /* SUB Rd, #Offset8 */ + { + rn = GET_REGISTER( ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT ); + op2 = ( insn & THUMB_INSN_IMM ); + //mame_printf_debug("%08x: Thumb instruction: SUB R%d, %02x\n", pc, ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT, op2); + rd = rn - op2; + SET_REGISTER( ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT, rd ); + HandleThumbALUSubFlags( rd, rn, op2 ); + } + else /* ADD Rd, #Offset8 */ + { + rn = GET_REGISTER( ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT ); + op2 = insn & THUMB_INSN_IMM; + rd = rn + op2; + //mame_printf_debug("%08x: Thumb instruction: ADD R%d, %02x\n", pc, ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT, op2); + SET_REGISTER( ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT, rd ); + HandleThumbALUAddFlags( rd, rn, op2 ); + } + break; + case 0x4: /* Rd & Rm instructions */ + switch( ( insn & THUMB_GROUP4_TYPE ) >> THUMB_GROUP4_TYPE_SHIFT ) + { + case 0x0: + switch( ( insn & THUMB_ALUOP_TYPE ) >> THUMB_ALUOP_TYPE_SHIFT ) + { + case 0x0: /* AND Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + SET_REGISTER( rd, GET_REGISTER(rd) & GET_REGISTER(rs) ); + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + break; + case 0x1: /* EOR Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + SET_REGISTER( rd, GET_REGISTER(rd) ^ GET_REGISTER(rs) ); + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + break; + case 0x2: /* LSL Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rrd = GET_REGISTER(rd); + offs = GET_REGISTER(rs) & 0x000000ff; + if (offs > 0) + { + if ( offs < 32 ) + { + SET_REGISTER( rd, rrd << offs ); + if( rrd & ( 1 << ( 31 - ( offs - 1 ) ) ) ) + { + ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C ); + } + else + { + ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C ); + } + } + else if( offs == 32 ) + { + SET_REGISTER( rd, 0 ); + if( rrd & 1 ) + { + ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C ); + } + else + { + ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C ); + } + } + else + { + SET_REGISTER( rd, 0 ); + ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C ); + } + } + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + break; + case 0x3: /* LSR Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rrd = GET_REGISTER(rd); + offs = GET_REGISTER(rs) & 0x000000ff; + if (offs > 0) + { + if( offs < 32 ) + { + SET_REGISTER( rd, rrd >> offs ); + if( rrd & ( 1 << ( offs - 1 ) ) ) + { + ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C ); + } + else + { + ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C ); + } + } + else if( offs == 32 ) + { + SET_REGISTER( rd, 0 ); + if( rrd & 0x80000000 ) + { + ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C ); + } + else + { + ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C ); + } + } + else + { + SET_REGISTER( rd, 0 ); + ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C ); + } + } + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + break; + case 0x4: /* ASR Rd, Rs */ + { + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rrs = GET_REGISTER(rs)&0xff; + rrd = GET_REGISTER(rd); + if (rrs != 0) + { + if (rrs >= 32) + { + if (rrd>>31) + { + ARM7_SetCPSR(GET_CPSR | ARM7_CPSR_C); + } + else + { + ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_C); + } + SET_REGISTER( rd, (GET_REGISTER(rd) & 0x80000000) ? 0xFFFFFFFF : 0x00000000 ); + } + else + { + if ((rrd>>(rs-1))&1) + { + ARM7_SetCPSR(GET_CPSR | ARM7_CPSR_C); + } + else + { + ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_C); + } + SET_REGISTER( rd, (rrd & 0x80000000) ? ((0xFFFFFFFF<<(32-rrs)) | (rrd>>rrs)) : (rrd>>rrs)); + } + } + ARM7_SetCPSR(GET_CPSR &~ (ARM7_CPSR_N | ARM7_CPSR_Z)); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + } + break; + case 0x5: /* ADC Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + op2=(GET_CPSR & ARM7_CPSR_C) ? 1 : 0; + rn=GET_REGISTER(rd) + GET_REGISTER(rs) + op2; + HandleThumbALUAddFlags( rn, GET_REGISTER(rd), ( GET_REGISTER(rs) ) ); //? + SET_REGISTER( rd, rn); + break; + case 0x6: /* SBC Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + op2=(GET_CPSR & ARM7_CPSR_C) ? 0 : 1; + rn=GET_REGISTER(rd) - GET_REGISTER(rs) - op2; + HandleThumbALUSubFlags( rn, GET_REGISTER(rd), ( GET_REGISTER(rs) ) ); //? + SET_REGISTER( rd, rn); + break; + case 0x7: /* ROR Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rrd = GET_REGISTER(rd); + imm = GET_REGISTER(rs) & 0x0000001f; + SET_REGISTER( rd, ( rrd >> imm ) | ( rrd << ( 32 - imm ) ) ); + if( rrd & ( 1 << ( imm - 1 ) ) ) + { + ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C ); + } + else + { + ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C ); + } + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + break; + case 0x8: /* TST Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) & GET_REGISTER(rs) ) ); + R15 += 2; + break; + case 0x9: /* NEG Rd, Rs - todo: check me */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rrs = GET_REGISTER(rs); + rn = 0 - rrs; + SET_REGISTER( rd, rn ); + HandleThumbALUSubFlags( GET_REGISTER(rd), 0, rrs ); + break; + case 0xa: /* CMP Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rn = GET_REGISTER(rd) - GET_REGISTER(rs); + HandleThumbALUSubFlags( rn, GET_REGISTER(rd), GET_REGISTER(rs) ); + break; + case 0xb: /* CMN Rd, Rs - check flags, add dasm */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rn = GET_REGISTER(rd) + GET_REGISTER(rs); + HandleThumbALUAddFlags( rn, GET_REGISTER(rd), GET_REGISTER(rs) ); + break; + case 0xc: /* ORR Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + SET_REGISTER( rd, GET_REGISTER(rd) | GET_REGISTER(rs) ); + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + break; + case 0xd: /* MUL Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + rn = GET_REGISTER(rd) * GET_REGISTER(rs); + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + SET_REGISTER( rd, rn ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( rn ) ); + R15 += 2; + break; + case 0xe: /* BIC Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + SET_REGISTER( rd, GET_REGISTER(rd) & (~GET_REGISTER(rs)) ); + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + break; + case 0xf: /* MVN Rd, Rs */ + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + op2 = GET_REGISTER(rs); + SET_REGISTER( rd, ~op2 ); + ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) ); + ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) ); + R15 += 2; + break; + default: + printf("%08x: G4-0 Undefined Thumb instruction: %04x %x\n", pc, insn, ( insn & THUMB_ALUOP_TYPE ) >> THUMB_ALUOP_TYPE_SHIFT); + R15 += 2; + break; + } + break; + case 0x1: + switch( ( insn & THUMB_HIREG_OP ) >> THUMB_HIREG_OP_SHIFT ) + { + case 0x0: /* ADD Rd, Rs */ + rs = ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; + rd = insn & THUMB_HIREG_RD; + switch( ( insn & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT ) + { + case 0x1: /* ADD Rd, HRs */ + SET_REGISTER( rd, GET_REGISTER(rd) + GET_REGISTER(rs+8) ); + // emulate the effects of pre-fetch + if (rs == 7) + { + SET_REGISTER(rd, GET_REGISTER(rd) + 4); + } + break; + case 0x2: /* ADD HRd, Rs */ + SET_REGISTER( rd+8, GET_REGISTER(rd+8) + GET_REGISTER(rs) ); + if (rd == 7) + { + R15 += 2; + change_pc(R15); + } + break; + case 0x3: /* Add HRd, HRs */ + SET_REGISTER( rd+8, GET_REGISTER(rd+8) + GET_REGISTER(rs+8) ); + // emulate the effects of pre-fetch + if (rs == 7) + { + SET_REGISTER(rd+8, GET_REGISTER(rd+8) + 4); + } + if (rd == 7) + { + R15 += 2; + change_pc(R15); + } + break; + default: + printf("%08x: G4-1-0 Undefined Thumb instruction: %04x %x\n", pc, insn, ( insn & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT ); + break; + } + R15 += 2; + break; + case 0x1: /* CMP */ + switch( ( insn & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT ) + { + case 0x0: /* CMP Rd, Rs */ + rs = GET_REGISTER( ( ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) ); + rd = GET_REGISTER( insn & THUMB_HIREG_RD ); + rn = rd - rs; + HandleThumbALUSubFlags( rn, rd, rs ); + break; + case 0x1: /* CMP Rd, Hs */ + rs = GET_REGISTER( ( ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) + 8 ); + rd = GET_REGISTER( insn & THUMB_HIREG_RD ); + rn = rd - rs; + HandleThumbALUSubFlags( rn, rd, rs ); + break; + case 0x2: /* CMP Hd, Rs */ + rs = GET_REGISTER( ( ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) ); + rd = GET_REGISTER( (insn & THUMB_HIREG_RD) + 8 ); + rn = rd - rs; + HandleThumbALUSubFlags( rn, rd, rs ); + break; + case 0x3: /* CMP Hd, Hs */ + rs = GET_REGISTER( ( ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) + 8 ); + rd = GET_REGISTER( (insn & THUMB_HIREG_RD) + 8 ); + rn = rd - rs; + HandleThumbALUSubFlags( rn, rd, rs ); + break; + default: + printf("%08x: G4-1 Undefined Thumb instruction: %04x %x\n", pc, insn, ( insn & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT); + R15 += 2; + break; + } + break; + case 0x2: /* MOV */ + switch( ( insn & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT ) + { + case 0x1: // MOV Rd, Hs + rs = ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; + rd = insn & THUMB_HIREG_RD; + if( rs == 7 ) + { + SET_REGISTER( rd, GET_REGISTER(rs + 8) + 4 ); + } + else + { + SET_REGISTER( rd, GET_REGISTER(rs + 8) ); + } + R15 += 2; + break; + case 0x2: // MOV Hd, Rs + rs = ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; + rd = insn & THUMB_HIREG_RD; + SET_REGISTER( rd + 8, GET_REGISTER(rs) ); + if( rd != 7 ) + { + R15 += 2; + } + else + { + R15 &= ~1; + change_pc(R15); + } + break; + case 0x3: // MOV Hd, Hs + rs = ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; + rd = insn & THUMB_HIREG_RD; + if (rs == 7) + { + SET_REGISTER( rd + 8, GET_REGISTER(rs+8)+4 ); + } + else + { + SET_REGISTER( rd + 8, GET_REGISTER(rs+8) ); + } + if( rd != 7 ) + { + R15 += 2; + } + + if( rd == 7 ) + { + R15 &= ~1; + change_pc(R15); + } + break; + default: + printf("%08x: G4-2 Undefined Thumb instruction: %04x (%x)\n", pc, insn, ( insn & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT); + R15 += 2; + break; + } + break; + case 0x3: + switch( ( insn & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT ) + { + case 0x0: + rd = ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; + addr = GET_REGISTER(rd); + if( addr & 1 ) + { + addr &= ~1; + } + else + { + ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_T); + if( addr & 2 ) + { + addr += 2; + } + } + R15 = addr; + break; + case 0x1: + addr = GET_REGISTER( ( ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) + 8 ); + if( ( ( ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) + 8 ) == 15 ) + { + addr += 2; + } + if( addr & 1 ) + { + addr &= ~1; + } + else + { + ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_T); + if( addr & 2 ) + { + addr += 2; + } + } + R15 = addr; + break; + default: + printf("%08x: G4-3 Undefined Thumb instruction: %04x\n", pc, insn); + R15 += 2; + break; + } + break; + default: + printf("%08x: G4-x Undefined Thumb instruction: %04x\n", pc, insn); + R15 += 2; + break; + } + break; + case 0x2: + case 0x3: + readword = arm7_read_32( ( R15 & ~2 ) + 4 + ( ( insn & THUMB_INSN_IMM ) << 2 ) ); + SET_REGISTER( ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT, readword ); + R15 += 2; + break; + default: + printf("%08x: G4-y Undefined Thumb instruction: %04x\n", pc, insn); + R15 += 2; + break; + } + break; + case 0x5: /* LDR* STR* */ + switch( ( insn & THUMB_GROUP5_TYPE ) >> THUMB_GROUP5_TYPE_SHIFT ) + { + case 0x0: /* STR Rd, [Rn, Rm] */ + rm = ( insn & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; + rn = ( insn & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; + rd = ( insn & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; + addr = GET_REGISTER(rn) + GET_REGISTER(rm); + arm7_write_32( addr, GET_REGISTER(rd) ); + R15 += 2; + break; + case 0x1: /* STRH Rd, [Rn, Rm] */ + rm = ( insn & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; + rn = ( insn & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; + rd = ( insn & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; + addr = GET_REGISTER(rn) + GET_REGISTER(rm); + arm7_write_16( addr, GET_REGISTER(rd) ); + R15 += 2; + break; + case 0x2: /* STRB Rd, [Rn, Rm] */ + rm = ( insn & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; + rn = ( insn & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; + rd = ( insn & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; + addr = GET_REGISTER(rn) + GET_REGISTER(rm); + arm7_write_8( addr, GET_REGISTER(rd) ); + R15 += 2; + break; + case 0x3: /* LDSB Rd, [Rn, Rm] todo, add dasm */ + rm = ( insn & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; + rn = ( insn & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; + rd = ( insn & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; + addr = GET_REGISTER(rn) + GET_REGISTER(rm); + op2 = arm7_read_8( addr ); + if( op2 & 0x00000080 ) + { + op2 |= 0xffffff00; + } + SET_REGISTER( rd, op2 ); + R15 += 2; + break; + case 0x4: /* LDR Rd, [Rn, Rm] */ + rm = ( insn & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; + rn = ( insn & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; + rd = ( insn & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; + addr = GET_REGISTER(rn) + GET_REGISTER(rm); + op2 = arm7_read_32( addr ); + SET_REGISTER( rd, op2 ); + R15 += 2; + break; + case 0x5: /* LDRH Rd, [Rn, Rm] */ + rm = ( insn & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; + rn = ( insn & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; + rd = ( insn & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; + addr = GET_REGISTER(rn) + GET_REGISTER(rm); + op2 = arm7_read_16( addr ); + SET_REGISTER( rd, op2 ); + R15 += 2; + break; + + case 0x6: /* LDRB Rd, [Rn, Rm] */ + rm = ( insn & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; + rn = ( insn & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; + rd = ( insn & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; + addr = GET_REGISTER(rn) + GET_REGISTER(rm); + op2 = arm7_read_8( addr ); + SET_REGISTER( rd, op2 ); + R15 += 2; + break; + case 0x7: /* LDSH Rd, [Rn, Rm] */ + rm = ( insn & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; + rn = ( insn & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; + rd = ( insn & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; + addr = GET_REGISTER(rn) + GET_REGISTER(rm); + op2 = arm7_read_16( addr ); + if( op2 & 0x00008000 ) + { + op2 |= 0xffff0000; + } + SET_REGISTER( rd, op2 ); + R15 += 2; + break; + default: + printf("%08x: G5 Undefined Thumb instruction: %04x\n", pc, insn); + R15 += 2; + break; + } + break; + case 0x6: /* Word Store w/ Immediate Offset */ + if( insn & THUMB_LSOP_L ) /* Load */ + { + rn = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = insn & THUMB_ADDSUB_RD; + offs = ( ( insn & THUMB_LSOP_OFFS ) >> THUMB_LSOP_OFFS_SHIFT ) << 2; + SET_REGISTER( rd, arm7_read_32(GET_REGISTER(rn) + offs) ); // fix + R15 += 2; + } + else /* Store */ + { + rn = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = insn & THUMB_ADDSUB_RD; + offs = ( ( insn & THUMB_LSOP_OFFS ) >> THUMB_LSOP_OFFS_SHIFT ) << 2; + arm7_write_32( GET_REGISTER(rn) + offs, GET_REGISTER(rd) ); + R15 += 2; + } + break; + case 0x7: /* Byte Store w/ Immeidate Offset */ + if( insn & THUMB_LSOP_L ) /* Load */ + { + rn = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = insn & THUMB_ADDSUB_RD; + offs = ( insn & THUMB_LSOP_OFFS ) >> THUMB_LSOP_OFFS_SHIFT; + SET_REGISTER( rd, arm7_read_8( GET_REGISTER(rn) + offs ) ); + R15 += 2; + } + else /* Store */ + { + rn = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = insn & THUMB_ADDSUB_RD; + offs = ( insn & THUMB_LSOP_OFFS ) >> THUMB_LSOP_OFFS_SHIFT; + arm7_write_8( GET_REGISTER(rn) + offs, GET_REGISTER(rd) ); + R15 += 2; + } + break; + case 0x8: /* Load/Store Halfword */ + if( insn & THUMB_HALFOP_L ) /* Load */ + { + imm = ( insn & THUMB_HALFOP_OFFS ) >> THUMB_HALFOP_OFFS_SHIFT; + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + SET_REGISTER( rd, arm7_read_16( GET_REGISTER(rs) + ( imm << 1 ) ) ); + R15 += 2; + } + else /* Store */ + { + imm = ( insn & THUMB_HALFOP_OFFS ) >> THUMB_HALFOP_OFFS_SHIFT; + rs = ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; + rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; + arm7_write_16( GET_REGISTER(rs) + ( imm << 1 ), GET_REGISTER(rd) ); + R15 += 2; + } + break; + case 0x9: /* Stack-Relative Load/Store */ + if( insn & THUMB_STACKOP_L ) + { + rd = ( insn & THUMB_STACKOP_RD ) >> THUMB_STACKOP_RD_SHIFT; + offs = (UINT8)( insn & THUMB_INSN_IMM ); + readword = arm7_read_32( GET_REGISTER(13) + ( (UINT32)offs << 2 ) ); + SET_REGISTER( rd, readword ); + R15 += 2; + } + else + { + rd = ( insn & THUMB_STACKOP_RD ) >> THUMB_STACKOP_RD_SHIFT; + offs = (UINT8)( insn & THUMB_INSN_IMM ); + arm7_write_32( GET_REGISTER(13) + ( (UINT32)offs << 2 ), GET_REGISTER(rd) ); + R15 += 2; + } + break; + case 0xa: /* Get relative address */ + if( insn & THUMB_RELADDR_SP ) /* ADD Rd, SP, #nn */ + { + rd = ( insn & THUMB_RELADDR_RD ) >> THUMB_RELADDR_RD_SHIFT; + offs = (UINT8)( insn & THUMB_INSN_IMM ) << 2; + SET_REGISTER( rd, GET_REGISTER(13) + offs ); + R15 += 2; + } + else /* ADD Rd, PC, #nn */ + { + rd = ( insn & THUMB_RELADDR_RD ) >> THUMB_RELADDR_RD_SHIFT; + offs = (UINT8)( insn & THUMB_INSN_IMM ) << 2; + SET_REGISTER( rd, ( ( R15 + 4 ) & ~2 ) + offs ); + R15 += 2; + } + break; + case 0xb: /* Stack-Related Opcodes */ + switch( ( insn & THUMB_STACKOP_TYPE ) >> THUMB_STACKOP_TYPE_SHIFT ) + { + case 0x0: /* ADD SP, #imm */ + addr = ( insn & THUMB_INSN_IMM ); + addr &= ~THUMB_INSN_IMM_S; + SET_REGISTER( 13, GET_REGISTER(13) + ( ( insn & THUMB_INSN_IMM_S ) ? -( addr << 2 ) : ( addr << 2 ) ) ); + R15 += 2; + break; + case 0x4: /* PUSH {Rlist} */ + for( offs = 7; offs >= 0; offs-- ) + { + if( insn & ( 1 << offs ) ) + { + SET_REGISTER( 13, GET_REGISTER(13) - 4 ); + arm7_write_32( GET_REGISTER(13), GET_REGISTER(offs) ); + } + } + R15 += 2; + break; + case 0x5: /* PUSH {Rlist}{LR} */ + SET_REGISTER( 13, GET_REGISTER(13) - 4 ); + arm7_write_32( GET_REGISTER(13), GET_REGISTER(14) ); + for( offs = 7; offs >= 0; offs-- ) + { + if( insn & ( 1 << offs ) ) + { + SET_REGISTER( 13, GET_REGISTER(13) - 4 ); + arm7_write_32( GET_REGISTER(13), GET_REGISTER(offs) ); + } + } + R15 += 2; + break; + case 0xc: /* POP {Rlist} */ + for( offs = 0; offs < 8; offs++ ) + { + if( insn & ( 1 << offs ) ) + { + SET_REGISTER( offs, arm7_read_32( GET_REGISTER(13) ) ); + SET_REGISTER( 13, GET_REGISTER(13) + 4 ); + } + } + R15 += 2; + break; + case 0xd: /* POP {Rlist}{PC} */ + for( offs = 0; offs < 8; offs++ ) + { + if( insn & ( 1 << offs ) ) + { + SET_REGISTER( offs, arm7_read_32( GET_REGISTER(13) ) ); + SET_REGISTER( 13, GET_REGISTER(13) + 4 ); + } + } + R15 = arm7_read_32( GET_REGISTER(13) ) & ~1; + SET_REGISTER( 13, GET_REGISTER(13) + 4 ); + break; + default: + printf("%08x: Gb Undefined Thumb instruction: %04x\n", pc, insn); + R15 += 2; + break; + } + break; + case 0xc: /* Multiple Load/Store */ + if( insn & THUMB_MULTLS ) /* Load */ + { + rd = ( insn & THUMB_MULTLS_BASE ) >> THUMB_MULTLS_BASE_SHIFT; + for( offs = 0; offs < 8; offs++ ) + { + if( insn & ( 1 << offs ) ) + { + SET_REGISTER( offs, arm7_read_32( (GET_REGISTER(rd)&0xfffffffc) ) ); + SET_REGISTER( rd, GET_REGISTER(rd) + 4 ); + } + } + R15 += 2; + } + else /* Store */ + { + rd = ( insn & THUMB_MULTLS_BASE ) >> THUMB_MULTLS_BASE_SHIFT; + for( offs = 0; offs < 8; offs++ ) + { + if( insn & ( 1 << offs ) ) + { + arm7_write_32( (GET_REGISTER(rd)&0xfffffffc), GET_REGISTER(offs) ); + SET_REGISTER( rd, GET_REGISTER(rd) + 4 ); + } + } + R15 += 2; + } + break; + case 0xd: /* Conditional Branch */ + offs = (INT8)( insn & THUMB_INSN_IMM ); + switch( ( insn & THUMB_COND_TYPE ) >> THUMB_COND_TYPE_SHIFT ) + { + case COND_EQ: + if( Z_IS_SET(GET_CPSR) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_NE: + if( Z_IS_CLEAR(GET_CPSR) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_CS: + if( C_IS_SET(GET_CPSR) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_CC: + if( C_IS_CLEAR(GET_CPSR) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_MI: + if( N_IS_SET(GET_CPSR) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_PL: + if( N_IS_CLEAR(GET_CPSR) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_VS: + if( V_IS_SET(GET_CPSR) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_VC: + if( V_IS_CLEAR(GET_CPSR) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_HI: + if( C_IS_SET(GET_CPSR) && Z_IS_CLEAR(GET_CPSR) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_LS: + if( C_IS_CLEAR(GET_CPSR) || Z_IS_SET(GET_CPSR) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_GE: + if( !(GET_CPSR & ARM7_CPSR_N) == !(GET_CPSR & ARM7_CPSR_V) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_LT: + if( !(GET_CPSR & ARM7_CPSR_N) != !(GET_CPSR & ARM7_CPSR_V) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_GT: + if( Z_IS_CLEAR(GET_CPSR) && ( !(GET_CPSR & ARM7_CPSR_N) == !(GET_CPSR & ARM7_CPSR_V) ) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_LE: + if( Z_IS_SET(GET_CPSR) || ( !(GET_CPSR & ARM7_CPSR_N) != !(GET_CPSR & ARM7_CPSR_V) ) ) + { + R15 += 4 + (offs << 1); + } + else + { + R15 += 2; + } + break; + case COND_AL: + printf("%08x: Undefined Thumb instruction: %04x (ARM9 reserved)\n", pc, insn); + R15 += 2; + break; + + case COND_NV: // SWI (this is sort of a "hole" in the opcode encoding) + ARM7_SetSWI(); +// R15 -= 4; + break; + } + break; + case 0xe: /* B #offs */ + if( insn & THUMB_BLOP_LO ) + { + addr = GET_REGISTER(14); + addr += ( insn & THUMB_BLOP_OFFS ) << 1; + addr &= 0xfffffffc; + SET_REGISTER( 14, ( R15 + 4 ) | 1 ); + R15 = addr; + } + else + { + offs = ( insn & THUMB_BRANCH_OFFS ) << 1; + if( offs & 0x00000800 ) + { + offs |= 0xfffff800; + } + R15 += 4 + offs; + } + break; + case 0xf: /* BL */ + if( insn & THUMB_BLOP_LO ) + { + addr = GET_REGISTER(14); + addr += ( insn & THUMB_BLOP_OFFS ) << 1; + SET_REGISTER( 14, ( R15 + 2 ) | 1 ); + R15 = addr; + } + else + { + addr = ( insn & THUMB_BLOP_OFFS ) << 12; + if( addr & ( 1 << 22 ) ) + { + addr |= 0xff800000; + } + addr += R15 + 4; + SET_REGISTER( 14, addr ); + R15 += 2; + } + break; + default: + printf("%08x: Undefined Thumb instruction: %04x\n", pc, insn); + R15 += 2; + break; + } + + return cycles; +} diff --git a/plugins/ao/eng_dsf/arm7thumb.h b/plugins/ao/eng_dsf/arm7thumb.h new file mode 100644 index 00000000..dabcd8ac --- /dev/null +++ b/plugins/ao/eng_dsf/arm7thumb.h @@ -0,0 +1,117 @@ +#ifndef _ARM7_THUMB_ +#define _ARM7_THUMB_ + +#define THUMB_INSN_TYPE ((UINT16) 0xf000) +#define THUMB_COND_TYPE ((UINT16) 0x0f00) +#define THUMB_GROUP4_TYPE ((UINT16) 0x0c00) +#define THUMB_GROUP5_TYPE ((UINT16) 0x0e00) +#define THUMB_GROUP5_RM ((UINT16) 0x01c0) +#define THUMB_GROUP5_RN ((UINT16) 0x0038) +#define THUMB_GROUP5_RD ((UINT16) 0x0007) +#define THUMB_ADDSUB_RNIMM ((UINT16) 0x01c0) +#define THUMB_ADDSUB_RS ((UINT16) 0x0038) +#define THUMB_ADDSUB_RD ((UINT16) 0x0007) +#define THUMB_INSN_ADDSUB ((UINT16) 0x0800) +#define THUMB_INSN_CMP ((UINT16) 0x0800) +#define THUMB_INSN_SUB ((UINT16) 0x0800) +#define THUMB_INSN_IMM_RD ((UINT16) 0x0700) +#define THUMB_INSN_IMM_S ((UINT16) 0x0080) +#define THUMB_INSN_IMM ((UINT16) 0x00ff) +#define THUMB_ADDSUB_TYPE ((UINT16) 0x0600) +#define THUMB_HIREG_OP ((UINT16) 0x0300) +#define THUMB_HIREG_H ((UINT16) 0x00c0) +#define THUMB_HIREG_RS ((UINT16) 0x0038) +#define THUMB_HIREG_RD ((UINT16) 0x0007) +#define THUMB_STACKOP_TYPE ((UINT16) 0x0f00) +#define THUMB_STACKOP_L ((UINT16) 0x0800) +#define THUMB_STACKOP_RD ((UINT16) 0x0700) +#define THUMB_ALUOP_TYPE ((UINT16) 0x03c0) +#define THUMB_BLOP_LO ((UINT16) 0x0800) +#define THUMB_BLOP_OFFS ((UINT16) 0x07ff) +#define THUMB_SHIFT_R ((UINT16) 0x0800) +#define THUMB_SHIFT_AMT ((UINT16) 0x07c0) +#define THUMB_HALFOP_L ((UINT16) 0x0800) +#define THUMB_HALFOP_OFFS ((UINT16) 0x07c0) +#define THUMB_BRANCH_OFFS ((UINT16) 0x07ff) +#define THUMB_LSOP_L ((UINT16) 0x0800) +#define THUMB_LSOP_OFFS ((UINT16) 0x07c0) +#define THUMB_MULTLS ((UINT16) 0x0800) +#define THUMB_MULTLS_BASE ((UINT16) 0x0700) +#define THUMB_RELADDR_SP ((UINT16) 0x0800) +#define THUMB_RELADDR_RD ((UINT16) 0x0700) +#define THUMB_INSN_TYPE_SHIFT 12 +#define THUMB_COND_TYPE_SHIFT 8 +#define THUMB_GROUP4_TYPE_SHIFT 10 +#define THUMB_GROUP5_TYPE_SHIFT 9 +#define THUMB_ADDSUB_TYPE_SHIFT 9 +#define THUMB_INSN_IMM_RD_SHIFT 8 +#define THUMB_STACKOP_TYPE_SHIFT 8 +#define THUMB_HIREG_OP_SHIFT 8 +#define THUMB_STACKOP_RD_SHIFT 8 +#define THUMB_MULTLS_BASE_SHIFT 8 +#define THUMB_RELADDR_RD_SHIFT 8 +#define THUMB_HIREG_H_SHIFT 6 +#define THUMB_HIREG_RS_SHIFT 3 +#define THUMB_ALUOP_TYPE_SHIFT 6 +#define THUMB_SHIFT_AMT_SHIFT 6 +#define THUMB_HALFOP_OFFS_SHIFT 6 +#define THUMB_LSOP_OFFS_SHIFT 6 +#define THUMB_GROUP5_RM_SHIFT 6 +#define THUMB_GROUP5_RN_SHIFT 3 +#define THUMB_GROUP5_RD_SHIFT 0 +#define THUMB_ADDSUB_RNIMM_SHIFT 6 +#define THUMB_ADDSUB_RS_SHIFT 3 +#define THUMB_ADDSUB_RD_SHIFT 0 + +#define THUMB_SIGN_BIT ((UINT32)(1<<31)) +#define THUMB_SIGN_BITS_DIFFER(a,b) (((a)^(b)) >> 31) + +#define N_IS_SET(pc) ((pc) & ARM7_CPSR_N) +#define Z_IS_SET(pc) ((pc) & ARM7_CPSR_Z) +#define C_IS_SET(pc) ((pc) & ARM7_CPSR_C) +#define V_IS_SET(pc) ((pc) & ARM7_CPSR_V) +#define I_IS_SET(pc) ((pc) & ARM7_CPSR_I) +#define F_IS_SET(pc) ((pc) & ARM7_CPSR_F) +#define T_IS_SET(pc) ((pc) & ARM7_CPSR_T) + +#define N_IS_CLEAR(pc) (!N_IS_SET(pc)) +#define Z_IS_CLEAR(pc) (!Z_IS_SET(pc)) +#define C_IS_CLEAR(pc) (!C_IS_SET(pc)) +#define V_IS_CLEAR(pc) (!V_IS_SET(pc)) +#define I_IS_CLEAR(pc) (!I_IS_SET(pc)) +#define F_IS_CLEAR(pc) (!F_IS_SET(pc)) +#define T_IS_CLEAR(pc) (!T_IS_SET(pc)) + +enum +{ + COND_EQ = 0, /* Z: equal */ + COND_NE, /* ~Z: not equal */ + COND_CS, COND_HS = 2, /* C: unsigned higher or same */ + COND_CC, COND_LO = 3, /* ~C: unsigned lower */ + COND_MI, /* N: negative */ + COND_PL, /* ~N: positive or zero */ + COND_VS, /* V: overflow */ + COND_VC, /* ~V: no overflow */ + COND_HI, /* C && ~Z: unsigned higher */ + COND_LS, /* ~C || Z: unsigned lower or same */ + COND_GE, /* N == V: greater or equal */ + COND_LT, /* N != V: less than */ + COND_GT, /* ~Z && (N == V): greater than */ + COND_LE, /* Z || (N != V): less than or equal */ + COND_AL, /* always */ + COND_NV /* never */ +}; + +#define GET_CPSR ARM7.Rx [ARM7_CPSR] + +#define GET_REGISTER(r) ARM7.Rx[(r)] +#define SET_REGISTER(r, v) ARM7.Rx[(r)] = (v) + +#define R15 ARM7.Rx[ARM7_PC] + + +// public function +int ARM7i_Thumb_Step(void); + +#endif + diff --git a/plugins/ao/eng_dsf/dc_hw.c b/plugins/ao/eng_dsf/dc_hw.c new file mode 100644 index 00000000..d6b10548 --- /dev/null +++ b/plugins/ao/eng_dsf/dc_hw.c @@ -0,0 +1,174 @@ +// dc_hw.c - Hardware found on the ARM7/AICA side of the Dreamcast + +#include "ao.h" +#include "dc_hw.h" +#include "aica.h" + +#define DK_CORE (1) + +#if DK_CORE +#include "arm7.h" +#else +#include "arm7core.h" +#endif + +uint8 dc_ram[8*1024*1024]; + +static void aica_irq(int irq) +{ + if (irq > 0) + { + #if DK_CORE + ARM7_SetFIQ(TRUE); + #else + set_irq_line(ARM7_FIRQ_LINE, 1); + #endif + } + else + { + #if DK_CORE + ARM7_SetFIQ(FALSE); + #else + set_irq_line(ARM7_FIRQ_LINE, 0); + #endif + } +} + +#define MIXER_PAN_LEFT 1 +#define MIXER_PAN_RIGHT 2 +#define MIXER(level,pan) ((level & 0xff) | ((pan & 0x03) << 8)) +#define YM3012_VOL(LVol,LPan,RVol,RPan) (MIXER(LVol,LPan)|(MIXER(RVol,RPan) << 16)) + +static struct AICAinterface aica_interface =
+{
+ 1, + { dc_ram, }, + { YM3012_VOL(100, MIXER_PAN_LEFT, 100, MIXER_PAN_RIGHT) }, + { aica_irq, }, +};
+ +uint8 dc_read8(int addr) +{ + if (addr < 0x800000) + { + return dc_ram[addr]; + } + + if ((addr >= 0x800000) && (addr <= 0x807fff)) + { + int foo = AICA_0_r((addr-0x800000)/2, 0); + + if (addr & 1) + { + return foo>>8; + } + else + { + return foo & 0xff; + } + } + + printf("R8 @ %x\n", addr); + return -1; +} + +uint16 dc_read16(int addr) +{ + if (addr < 0x800000) + { + return dc_ram[addr] | (dc_ram[addr+1]<<8); + } + + if ((addr >= 0x800000) && (addr <= 0x807fff)) + { + return AICA_0_r((addr-0x800000)/2, 0); + } + + printf("R16 @ %x\n", addr); + return -1; +} + +uint32 dc_read32(int addr) +{ + if (addr < 0x800000) + { + return dc_ram[addr] | (dc_ram[addr+1]<<8) | (dc_ram[addr+2]<<16) | (dc_ram[addr+3]<<24); + } + + if ((addr >= 0x800000) && (addr <= 0x807fff)) + { + addr &= 0x7fff; + return AICA_0_r(addr/2, 0) & 0xffff; + } + +// printf("R32 @ %x\n", addr); + return 0; +} + +void dc_write8(int addr, uint8 data) +{ + if (addr < 0x800000) + { + dc_ram[addr] = data; + return; + } + + if ((addr >= 0x800000) && (addr <= 0x807fff)) + { + addr -= 0x800000; + if ((addr & 1)) + AICA_0_w(addr>>1, data<<8, 0x00ff); + else + AICA_0_w(addr>>1, data, 0xff00); + return; + } + + printf("W8 %x @ %x\n", data, addr); +} + +void dc_write16(int addr, uint16 data) +{ + if (addr < 0x800000) + { + dc_ram[addr] = data&0xff; + dc_ram[addr+1] = (data>>8) & 0xff; + return; + } + + if ((addr >= 0x800000) && (addr <= 0x807fff)) + { + AICA_0_w((addr-0x800000)/2, data, 0); + return; + } + + printf("W16 %x @ %x\n", data, addr); +} + +void dc_write32(int addr, uint32 data) +{ + if (addr < 0x800000) + { + dc_ram[addr] = data&0xff; + dc_ram[addr+1] = (data>>8) & 0xff; + dc_ram[addr+2] = (data>>16) & 0xff; + dc_ram[addr+3] = (data>>24) & 0xff; + return; + } + + if ((addr >= 0x800000) && (addr <= 0x807fff)) + { + addr -= 0x800000; + AICA_0_w((addr>>1), data&0xffff, 0x0000); + AICA_0_w((addr>>1)+1, data>>16, 0x0000); + return; + } + + printf("W32 %x @ %x\n", data, addr); +} + +void dc_hw_init(void) +{ + aica_interface.region[0] = dc_ram; + aica_start(&aica_interface); +} + diff --git a/plugins/ao/eng_dsf/dc_hw.h b/plugins/ao/eng_dsf/dc_hw.h new file mode 100644 index 00000000..b17de9e1 --- /dev/null +++ b/plugins/ao/eng_dsf/dc_hw.h @@ -0,0 +1,9 @@ +#ifndef _DC_HW_H_ +#define _DC_HW_H_ + +extern uint8 dc_ram[8*1024*1024]; + +void dc_hw_init(void); + +#endif + diff --git a/plugins/ao/eng_dsf/eng_dsf.c b/plugins/ao/eng_dsf/eng_dsf.c new file mode 100644 index 00000000..f6ac91ed --- /dev/null +++ b/plugins/ao/eng_dsf/eng_dsf.c @@ -0,0 +1,256 @@ +// +// Audio Overload +// Emulated music player +// +// (C) 2000-2008 Richard F. Bannister +// + +// +// eng_dsf.c +// + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "ao.h" +#include "eng_protos.h" +#include "corlett.h" +#include "dc_hw.h" +#include "aica.h" + +#define DEBUG_LOADER (0) +#define DK_CORE (1) + +#if DK_CORE +#include "arm7.h" +#else +#include "arm7core.h" +#endif + +static corlett_t *c = NULL; +static char psfby[256]; +static uint32 decaybegin, decayend, total_samples; + +void *aica_start(const void *config); +void AICA_Update(void *param, INT16 **inputs, INT16 **buf, int samples); + +int32 dsf_start(uint8 *buffer, uint32 length) +{ + uint8 *file, *lib_decoded, *lib_raw_file; + uint32 offset, plength, lengthMS, fadeMS; + uint64 file_len, lib_len, lib_raw_length; + corlett_t *lib; + char *libfile; + int i; + + // clear Dreamcast work RAM before we start scribbling in it + memset(dc_ram, 0, 8*1024*1024); + + // Decode the current SSF + if (corlett_decode(buffer, length, &file, &file_len, &c) != AO_SUCCESS) + { + return AO_FAIL; + } + + #if DEBUG_LOADER + printf("%d bytes decoded\n", file_len); + #endif + + // Get the library file, if any + for (i=0; i<9; i++) { + libfile = i ? c->libaux[i-1] : c->lib; + if (libfile[0] != 0) + { + uint64 tmp_length; + + #if DEBUG_LOADER + printf("Loading library: %s\n", c->lib); + #endif + if (ao_get_lib(libfile, &lib_raw_file, &tmp_length) != AO_SUCCESS) + { + return AO_FAIL; + } + lib_raw_length = tmp_length; + + if (corlett_decode(lib_raw_file, lib_raw_length, &lib_decoded, &lib_len, &lib) != AO_SUCCESS) + { + free(lib_raw_file); + return AO_FAIL; + } + + // Free up raw file + free(lib_raw_file); + + // patch the file into ram + offset = lib_decoded[0] | lib_decoded[1]<<8 | lib_decoded[2]<<16 | lib_decoded[3]<<24; + memcpy(&dc_ram[offset], lib_decoded+4, lib_len-4); + + // Dispose the corlett structure for the lib - we don't use it + free(lib); + } + } + + // now patch the file into RAM over the libraries + offset = file[3]<<24 | file[2]<<16 | file[1]<<8 | file[0]; + memcpy(&dc_ram[offset], file+4, file_len-4); + + free(file); + + // Finally, set psfby/ssfby tag + strcpy(psfby, "n/a"); + if (c) + { + for (i = 0; i < MAX_UNKNOWN_TAGS; i++) + { + if ((!strcasecmp(c->tag_name[i], "psfby")) || (!strcasecmp(c->tag_name[i], "ssfby"))) + strcpy(psfby, c->tag_data[i]); + } + } + + #if DEBUG_LOADER && 1 + { + FILE *f; + + f = fopen("dcram.bin", "wb"); + fwrite(dc_ram, 2*1024*1024, 1, f); + fclose(f); + } + #endif + + #if DK_CORE + ARM7_Init(); + #else + arm7_init(0, 45000000, NULL, NULL); + arm7_reset(); + #endif + dc_hw_init(); + + // now figure out the time in samples for the length/fade + lengthMS = psfTimeToMS(c->inf_length); + fadeMS = psfTimeToMS(c->inf_fade); + total_samples = 0; + + if (lengthMS == 0) + { + lengthMS = ~0; + } + + if (lengthMS == ~0) + { + decaybegin = lengthMS; + } + else + { + lengthMS = (lengthMS * 441) / 10; + fadeMS = (fadeMS * 441) / 10; + + decaybegin = lengthMS; + decayend = lengthMS + fadeMS; + } + + return AO_SUCCESS; +} + +int32 dsf_gen(int16 *buffer, uint32 samples) +{ + int i; + int16 output[44100/30], output2[44100/30]; + int16 *stereo[2]; + int16 *outp = buffer; + int opos; + + opos = 0; + for (i = 0; i < samples; i++) + { + #if DK_CORE + ARM7_Execute((33000000 / 60 / 4) / 735); + #else + arm7_execute((33000000 / 60 / 4) / 735); + #endif + stereo[0] = &output[opos]; + stereo[1] = &output2[opos]; + AICA_Update(NULL, NULL, stereo, 1); + opos++; + } + + for (i = 0; i < samples; i++) + { + // process the fade tags + if (total_samples >= decaybegin) + { + if (total_samples >= decayend) + { + // song is done here, signal your player appropriately! +// ao_song_done = 1; + output[i] = 0; + output2[i] = 0; + } + else + { + int32 fader = 256 - (256*(total_samples - decaybegin)/(decayend-decaybegin)); + output[i] = (output[i] * fader)>>8; + output2[i] = (output2[i] * fader)>>8; + + total_samples++; + } + } + else + { + total_samples++; + } + + *outp++ = output[i]; + *outp++ = output2[i]; + } + + return AO_SUCCESS; +} + +int32 dsf_stop(void) +{ + return AO_SUCCESS; +} + +int32 dsf_command(int32 command, int32 parameter) +{ + switch (command) + { + case COMMAND_RESTART: + return AO_SUCCESS; + + } + return AO_FAIL; +} + +int32 dsf_fill_info(ao_display_info *info) +{ + if (c == NULL) + return AO_FAIL; + + strcpy(info->title[1], "Name: "); + sprintf(info->info[1], "%s", c->inf_title); + + strcpy(info->title[2], "Game: "); + sprintf(info->info[2], "%s", c->inf_game); + + strcpy(info->title[3], "Artist: "); + sprintf(info->info[3], "%s", c->inf_artist); + + strcpy(info->title[4], "Copyright: "); + sprintf(info->info[4], "%s", c->inf_copy); + + strcpy(info->title[5], "Year: "); + sprintf(info->info[5], "%s", c->inf_year); + + strcpy(info->title[6], "Length: "); + sprintf(info->info[6], "%s", c->inf_length); + + strcpy(info->title[7], "Fade: "); + sprintf(info->info[7], "%s", c->inf_fade); + + strcpy(info->title[8], "Ripper: "); + sprintf(info->info[8], "%s", psfby); + + return AO_SUCCESS; +} |