File:  [Generator SEGA Genesis emulator] / generator / extra / compile-arm.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed Mar 4 04:46:43 2020 UTC (6 years, 4 months ago) by root
Branches: MAIN, JamesPonder
CVS tags: v0_32, HEAD
0.32

/*****************************************************************************/
/*     Generator - Sega Genesis emulation - (c) James Ponder 1997-1998       */
/*****************************************************************************/
/*                                                                           */
/* compile-arm.c                                                             */
/*                                                                           */
/*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>

#include <sys/swis.h>
#include <sys/os.h>

#include <generator.h>
#include <cpu68k.h>
#include <mem68k.h>

#define MAXBLOCKSIZE 10240
#define true 1
#define false 0

/* static global variables */

static uint8 *curpos;
static sint32 regstate[6]; /* 0 = unused, 1 = used, 2 = updated */
static sint32 regnum[6];   /* register number or -1 */
static sint32 regtimer[6]; /* instructions since used */

/* forward references */

inline uint8 *compile_w(uint32 i)
{
  *(uint32 *)curpos = i;
  curpos+= 4;
  return curpos;
}

/*** compile_initregs - set all registers to unused ***/

void compile_initregs(void)
{
  int i;

  for (i = 1; i < 6; i++) {
    regstate[i] = 0;
    regnum[i] = -1;
    regtimer[i] = 0;
  }
}

/*** compile_getreg - given a 68000 register or -1 return an ARM register ***/

int compile_getreg(int reg)
{
  int i;
  int free = -1;
  int reg_last = -1;
  int reg_lasttime = -1;

  /* check for an ARM register already holding the 68000 register or a free
     ARM register if we can't find it */

  for (i = 1; i < 6; i++) {
    if (reg != -1 && regstate[i] && regnum[i] == reg) {
      regtimer[i] = 0;
      return i;
    }
    if (!regstate[i]) {
      free = i;
    }
  }
  if (free == -1) {
    /* no free registers, find a register not holding a specific 68000
       register if pos - if we can't find one then use the oldest 68000
       register cached even if it was used this instruction */

    for (i = 1; i < 6; i++) {
      if (regtimer[i] && regnum[i] == -1) {
	free = i;
      } else {
	if (regtimer[i] >= reg_lasttime) {
	  /* we always prefer higher registers */
	  reg_last = i;
	  reg_lasttime = regtimer[i];
	}
      }
    }
    if (free == -1) {
      free = reg_last;
      if (free == -1) {
	fprintf(stderr, "free: %d\n", free);
	fprintf(stderr, "reg_last: %d\n", reg_last);
	fprintf(stderr, "reg_lasttime: %d\n", reg_lasttime);
	fprintf(stderr, "5 state: %d\n", regstate[5]);
	fprintf(stderr, "5 num: %d\n", regnum[5]);
	fprintf(stderr, "5 timer: %d\n", regtimer[5]);
	compile_w(0);
	compile_w((uint32)(-1));
	compile_finalregs();
	fprintf(stderr, "5 state: %d\n", regstate[5]);
	fprintf(stderr, "5 num: %d\n", regnum[5]);
	fprintf(stderr, "5 timer: %d\n", regtimer[5]);
	fprintf(stderr, "Insufficient registers\n");
	exit(1);
      }
      if (regstate[reg_last] == 2) {
	/* STR r(reg_last),[r8,#x] */
	compile_w(0xE5880000 | (regnum[reg_last]*4) | reg_last<<12);
      }
    }
  }
  regstate[free] = 1;
  regnum[free] = reg;
  regtimer[free] = 0;
  if (reg != -1) {
    compile_w(0xE5980000 | (reg*4) | free<<12); /* LDR rfree,[r8,#x] */
  }
  return free;
}

/*** compile_findreg - given a 68000 register return an ARM register ***/

int compile_findreg(int reg)
{
  int i;

  for (i = 1; i < 6; i++) {
    if (regstate[i] && regnum[i] == reg) {
      return i;
    }
  }
  return -1;
}

/*** compile_updatereg - we have changed a register ***/

void compile_updatereg(int reg)
{
  if (!regstate[reg]) {
    fprintf(stderr, "Compiler error - updatereg %d\n", reg);
    exit(1);
  }
  regstate[reg] = 2;
}

/*** compile_inctimer - update timers on registers ***/

void compile_inctimer(void) {
  int i;

  for (i = 1; i < 6; i++) {
    if (regstate[i]) {
      if (regnum[i] == -1)
	regstate[i] = 0;
      else
	regtimer[i]++;
    }
  }
}

/*** compile_finalregs - write back all changed registers ***/

void compile_finalregs(void) {
  int i;

  for (i = 1; i < 6; i++) {
    if (regstate[i] == 2 && regnum[i] != -1) {
      compile_w(0xE5880000 | (regnum[i]*4) | i<<12); /* STR ri,[r8,#x] */
    }
    regstate[i] = 0;
    regnum[i] = -1;
    regtimer[i] = 0;
  }
}

void compile_ea(t_iib *iib, t_ipc *ipc, t_type type, int update, int *reg)
{
  t_datatype datatype = type ? iib->dtype : iib->stype;
  int reg68;
  int a, b, c, pos;

  /* compile EA to register(s) regea/regval */

  switch(datatype) {
  case dt_Dreg:
  case dt_Areg:
    *reg = -1;
    break;
  case dt_Aind:
    reg68 = 8 + ((ipc->opcode >> (type ? iib->dbitpos : iib->sbitpos)) & 7);
    a = compile_getreg(reg68);
    b = compile_getreg(-1); /* effective address */
    compile_w(0xE1A00000 | b<<12 | a); /* MOV rb,ra */
    *reg = b;
    break;
  case dt_Ainc:
    reg68 = 8 + ((ipc->opcode >> (type ? iib->dbitpos : iib->sbitpos)) & 7);
    a = compile_getreg(reg68); /* 68000 register */
    if (update) {
      b = compile_getreg(-1);       /* effective address */
      regnum[a] = -1;
      regnum[b] = reg68; /* swap */
      /* ADD rb,ra,#size */
      compile_w(0xE2800000 | 1<<(iib->size-1) | a<<16 | b<<12);
      compile_updatereg(b);
    }
    *reg = a;
    break;
  case dt_Adec:
    reg68 = 8 + ((ipc->opcode >> (type ? iib->dbitpos : iib->sbitpos)) & 7);
    a = compile_getreg(reg68); /* 68000 register */
    if (update) {
      /* SUB ra,ra,#size */
      compile_w(0xE2400000 | 1<<(iib->size-1) | a<<16 | a<<12);
      compile_updatereg(a);
    }
    *reg = a;
    break;
  case dt_Adis:
    reg68 = 8 + ((ipc->opcode >> (type ? iib->dbitpos : iib->sbitpos)) & 7);
    a = compile_getreg(reg68);    /* 68000 register */
    b = compile_getreg(-1);       /* effective address */
    pos = (uint32)(type ? &ipc->dst : &ipc->src) - (uint32)ipc;
    /* LDR rb,[r6,#pos] ADD rb,rb,ra */
    compile_w(0xE5960000 | pos | b<<12);
    compile_w(0xE0800000 | b<<16 | b<<12 | a);
    *reg = b;
    break;
  case dt_Aidx:
    reg68 = 8 + ((ipc->opcode >> (type ? iib->dbitpos : iib->sbitpos)) & 7);
    b = compile_getreg(-1);       /* effective address */
    c = compile_getreg(((type ? ipc->dst : ipc->src)>>28) & 15); /* idx reg */
    pos = (uint32)(type ? &ipc->dst : &ipc->src) - (uint32)ipc;
    /* LDR rb,[r6,#pos] MOV rb,rb,LSL#8 MOV rb,rb,ASR#8 */
    compile_w(0xE5960000 | pos | b<<12);
    compile_w(0xE1A00400 | b<<12 | b);
    compile_w(0xE1A00440 | b<<12 | b);
    if ((ipc->src>>27) & 1) {
      /* ADD rb,rb,rc */
      compile_w(0xE0800000 | b<<16 | b<<12 | c);
    } else {
      /* MOV r14,rc,LSL#16 MOV r14,r14,ASR#16 ADD rb,rb,r14 */
      compile_w(0xE1A0E800 | c);
      compile_w(0xE1A0E84E);
      compile_w(0xE080000E | b<<16 | b<<12);
    }
    a = compile_getreg(reg68);    /* 68000 register */
    /* ADD rb,rb,ra */
    compile_w(0xE0800000 | b<<16 | b<<12 | a);
    *reg = b;
    break;
  case dt_AbsW:
  case dt_AbsL:
  case dt_Pdis:
    a = compile_getreg(-1); /* effective address */
    pos = (uint32)(type ? &ipc->dst : &ipc->src) - (uint32)ipc;
    /* LDR ra,[r6,#pos] */
    compile_w(0xE5960000 | pos | a<<12);
    *reg = a;
    break;
  case dt_Pidx:
    b = compile_getreg(-1);       /* effective address */
    c = compile_getreg(((type ? ipc->dst : ipc->src)>>28) & 15); /* idx reg */
    pos = (uint32)(type ? &ipc->dst : &ipc->src) - (uint32)ipc;
    /* LDR rb,[r6,#pos] MOV rb,rb,LSL#8 MOV rb,rb,ASL#8 */
    compile_w(0xE5960000 | pos | b<<12);
    compile_w(0xE1A00400 | b<<12 | b);
    compile_w(0xE1A00440 | b<<12 | b);
    if ((ipc->src>>27) & 1) {
      /* ADD rb,rb,rc */
      compile_w(0xE0800000 | b<<16 | b<<12 | c);
    } else {
      /* MOV r14,rc,LSL#16 MOV r14,r14,ASR#16 ADD rb,rb,r14 */
      compile_w(0xE1A0E800 | c);
      compile_w(0xE1A0E84E);
      compile_w(0xE080000E | b<<16 | b<<12);
    }
    *reg = b;
    break;
  case dt_ImmB:
  case dt_ImmW:
  case dt_ImmL:
  case dt_ImmS:
  case dt_Imm3:
  case dt_Imm4:
  case dt_Imm8:
  case dt_Imm8s:
    *reg = -1;
    break;
  default:
    fprintf(stderr, "Invalid datatype %d\n", datatype);
    exit(1);
  }
}

void compile_eaval(t_iib *iib, t_ipc *ipc, t_type type, int update,
		    int eareg, int *eaval)
{
  t_datatype datatype = type ? iib->dtype : iib->stype;
  int reg68;
  int a, b, pos;

  /* compile EA value */

  switch(datatype) {
  case dt_Dreg:
  case dt_Areg:
    reg68 = ((datatype == dt_Areg ? 8 : 0) +
	     ((ipc->opcode >> (type ? iib->dbitpos : iib->sbitpos)) & 7));
    a = compile_getreg(reg68);
    b = compile_getreg(-1);
    switch(iib->size) {
    case sz_byte:
      compile_w(0xE20000FF | b<<12 | a<<16); /* AND rb,ra,#&FF */
      break;
    case sz_word:
      compile_w(0xE1A00800 | b<<12 | a); /* MOV rb,ra,LSL#16 */
      compile_w(0xE1A00820 | b<<12 | b); /* MOV rb,rb,LSR#16 */
      break;
    case sz_long:
      compile_w(0xE1A00000 | b<<12 | a); /* MOV rb,ra */
      break;
    default:
      fprintf(stderr, "Invalid size %d\n", iib->size);
      exit(1);
    }
    *eaval = b;
    break;
  case dt_Aind:
  case dt_Ainc:
  case dt_Adec:
  case dt_Adis:
  case dt_Aidx:
  case dt_AbsW:
  case dt_AbsL:
  case dt_Pdis:
  case dt_Pidx:
    a = compile_getreg(-1);
    compile_w(0xE92D100E);             /* STMFD r13!,{r1-r3,r12} */
    compile_w(0xE3C004FF | eareg<<16); /* BIC r0,eareg,#&FF000000 */
    compile_w(0xE1A03620);             /* MOV r3,r0,LSR#12 */
    compile_w(0xE59F2004);             /* LDR r2,[PC,#4] */
    compile_w(0xE28FE004);             /* ADD r14,PC,#4 */
    compile_w(0xE792F103);             /* LDR PC,[r2,r3,LSL#2]; */
    /* EQUD <array address> */
    switch (iib->size) {
    case sz_byte:
      compile_w((uint32)mem68k_fetch_byte);
      break;
    case sz_word:
      compile_w((uint32)mem68k_fetch_word);
      break;
    case sz_long:
      compile_w((uint32)mem68k_fetch_long);
      break;
    default:
      fprintf(stderr, "Invalid size %d\n", iib->size);
      exit(1);
    }
    compile_w(0xE8BD100E);         /* LDMFD r13!,{r1-r3,r12} */
    compile_w(0xE1A00000 | a<<12); /* MOV ra, r0 */
    *eaval = a;
    break;
  case dt_ImmB:
    a = compile_getreg(-1);
    /* MOV eaval,#data */
    compile_w(0xE3A00000 | (type ? ipc->dst : ipc->src) | a<<12);
    *eaval = a;
    break;
  case dt_ImmW:
  case dt_ImmL:
  case dt_Imm3: /* I want optimising */
  case dt_Imm4:
  case dt_Imm8:
  case dt_Imm8s:
    a = compile_getreg(-1);
    pos = (uint32)(type ? &ipc->dst : &ipc->src) - (uint32)ipc;
    /* LDR eaval,[r6,#pos] */
    compile_w(0xE5960000 | pos | a<<12);
    *eaval = a;
    break;
  case dt_ImmS:
    a = compile_getreg(-1);
    /* MOV a,#val */
    compile_w(0xE3A00000 | iib->immvalue | a<<12);
    *eaval = a;
    break;
  default:
    fprintf(stderr, "Invalid datatype %d\n", datatype);
    exit(1);
  }
}

void compile_eastore(t_iib *iib, t_ipc *ipc, t_type type, int eareg,
		     int eaval)
{
  t_datatype datatype = type ? iib->dtype : iib->stype;
  int reg68;
  int a, b, pos;

  /* store EA value */

  switch(datatype) {
  case dt_Dreg:
  case dt_Areg:
    reg68 = ((datatype == dt_Areg ? 8 : 0) +
	     ((ipc->opcode >> (type ? iib->dbitpos : iib->sbitpos)) & 7));
    a = compile_findreg(reg68);
    if (a == eaval) {
      fprintf(stderr, "Compiler error - eastore\n");
      exit(1);
    }
    switch(iib->size) {
    case sz_byte:
      if (a == -1)
	a = compile_getreg(reg68);
      compile_w(0xE3C000FF | a<<16 | a<<12); /* BIC ra,ra,#&FF */
      compile_w(0xE1800000 | a<<16 | a<<12 | eaval); /* ORR ra,ra,eaval */
      compile_updatereg(a);
      break;
    case sz_word:
      if (a == -1)
	a = compile_getreg(reg68);
      compile_w(0xE1A00820 | a<<12 | a); /* MOV ra,ra,LSR#16 */
      /*  ORR ra,eaval,ra,LSL#16 */
      compile_w(0xE1800800 | eaval<<16 | a<<12 | a);
      compile_updatereg(a);
      break;
    case sz_long:
      if (a != -1) {
	/* the 68000 reg is in an ARM register */
	regnum[a] = -1;
	regnum[eaval] = reg68;
	regstate[eaval] = 2; /* needs writing back */
      } else {
	/* the 68000 reg is not in an ARM reg and we're writing all 32
	   bits so we can just name the ARM reg as the 68000 reg */
	regnum[eaval] = reg68;
	regstate[eaval] = 2; /* needs writing back */
      }
      break;
    default:
      fprintf(stderr, "Invalid size %d\n", iib->size);
      exit(1);
    }
    break;
  case dt_Aind:
  case dt_Ainc:
  case dt_Adec:
  case dt_Adis:
  case dt_Aidx:
  case dt_AbsW:
  case dt_AbsL:
  case dt_Pdis:
  case dt_Pidx:
    compile_w(0xE92D100E);             /* STMFD r13!,{r1-r3,r12} */
    compile_w(0xE3C004FF | eareg<<16); /* BIC r0,eareg,#&FF000000 */
    compile_w(0xE1A01000 | eaval);     /* MOV r1,eaval */
    compile_w(0xE1A03620);             /* MOV r3,r0,LSR#12 */
    compile_w(0xE59F2004);             /* LDR r2,[PC,#4] */
    compile_w(0xE28FE004);             /* ADD r14,PC,#4 */
    compile_w(0xE792F103);             /* LDR PC,[r2,r3,LSL#2]; */
    /* EQUD <array address> */
    switch (iib->size) {
    case sz_byte:
      compile_w((uint32)mem68k_store_byte);
      break;
    case sz_word:
      compile_w((uint32)mem68k_store_word);
      break;
    case sz_long:
      compile_w((uint32)mem68k_store_long);
      break;
    default:
      fprintf(stderr, "Invalid size %d\n", iib->size);
      exit(1);
    }
    compile_w(0xE8BD100E);         /* LDMFD r13!,{r1-r3,r12} */
    break;
  case dt_ImmB:
  case dt_ImmW:
  case dt_ImmL:
  case dt_Imm3: /* I want optimising */
  case dt_Imm4:
  case dt_Imm8:
  case dt_Imm8s:
  case dt_ImmS:
  default:
    fprintf(stderr, "Invalid datatype %d\n", datatype);
    exit(1);
  }
}
    
void compile_clrflag_v(t_iib *iib)
{
  compile_w(0xE3C99000 | SR_VFLAG); /* BIC r9,r9,#SR_VFLAG */
}

void compile_clrflag_c(t_iib *iib)
{
  compile_w(0xE3C99000 | SR_CFLAG); /* BIC r9,r9,#SR_CFLAG */
}

void compile_stdflag_n(t_iib *iib, int reg)
{
  switch(iib->size) {
  case sz_byte:
    compile_w(0xE3100080 | reg<<16); /* TST reg,#1<<7 */
    break;
  case sz_word:
    compile_w(0xE3100902 | reg<<16); /* TST reg,#1<<15 */
    break;
  case sz_long:
    compile_w(0xE3100102 | reg<<16); /* TST reg,#1<<31 */
    break;
  default:
    fprintf(stderr, "Invalid size %d\n", iib->size);
    exit(1);
  }
  compile_w(0x13899000 | SR_NFLAG); /* ORRNE r9,r9,#SR_NFLAG */
  compile_w(0x03C99000 | SR_NFLAG); /* BICEQ r9,r9,#SR_NFLAG */
}

void compile_stdflag_z(t_iib *iib, int reg)
{
  switch(iib->size) {
  case sz_byte:
    compile_w(0xE1B0EC00 | reg); /* MOVS r14,r1,LSL#24 */
    break;
  case sz_word:
    compile_w(0xE1B0E800 | reg); /* MOVS r14,r1,LSL#16 */
    break;
  case sz_long:
    compile_w(0xE1B00000 | reg<<12 | reg); /* MOVS r1,r1 */
    break;
  default:
    fprintf(stderr, "Invalid size %d\n", iib->size);
    exit(1);
  }
  compile_w(0x03899000 | SR_ZFLAG); /* ORREQ r9,r9,#SR_ZFLAG */
  compile_w(0x13C99000 | SR_ZFLAG); /* BICNE r9,r9,#SR_ZFLAG */
}

/*
 *  R0-R5 = work registers
 *  R6 = IPC
 *  R7 = PC
 *  R8-> registers
 *  R9 = SR
 */

char *compile_make(t_ipclist *list)
{
  int instrs = 0;
  uint8 *block = malloc(MAXBLOCKSIZE);
  t_ipc *ipc = (t_ipc *)(list+1);
  t_iib *iib;
  int len;
  uint8 *stmia, *p;
  uint32 r[10];
  int srcreg, dstreg, srcval, dstval;
  static int blockno = 1;
  static char tmp[256];
  FILE *file;
  int normal;

  *(uint32 *)0x7004 = (uint32)block;
  *(uint32 *)0x7008 = (uint32)list->pc;

  if (!block) {
    fprintf(stderr, "Out of memory!\n");
    exit(1);
  }
  curpos = block;

  compile_w(0xE92D4070); /* STMDB r13!,{r4-r6,r14} */
  compile_w(0xE1A06000); /* MOV r6,r0 */

  compile_initregs();

  while(*(int *)ipc) {
    if ((curpos - block) > MAXBLOCKSIZE-1024) {
      fprintf(stderr, "Block too short\n");
      exit(1);
    }
    instrs++;
    iib = cpu68k_iibtable[ipc->opcode];

    normal = 1;
    
    switch(iib->mnemonic) {
    case i_MOVE:
      if ((iib->stype == dt_Dreg || iib->stype == dt_Areg) &&
	  (iib->dtype == dt_Dreg || iib->dtype == dt_Areg)) {
	compile_ea(iib, ipc, tp_src, true, &srcreg);
	compile_eaval(iib, ipc, tp_src, true, srcreg, &srcval);
	if (srcreg != -1) 
	  regtimer[srcreg]++;
	compile_ea(iib, ipc, tp_dst, true, &dstreg);
	compile_eastore(iib, ipc, tp_dst, dstreg, srcval);
	if (ipc->set & IIB_FLAG_V)
	  compile_clrflag_v(iib);
	if (ipc->set & IIB_FLAG_C)
	  compile_clrflag_c(iib);
	if (ipc->set & IIB_FLAG_N)
	  compile_stdflag_n(iib, srcval);
	if (ipc->set & IIB_FLAG_Z)
	  compile_stdflag_z(iib, srcval);
	compile_w(0xE2877000 | 2*iib->wordlen); /* ADD r7,r7,#wordlen */
	normal = 0;
      }
      break;
    default:
      break;
    }
    if (normal) {
      compile_finalregs();
      compile_w(0xE1A00006); /* MOV r0,r6 */
      /* BL ipc->function */
      compile_w(0xEB000000 + ( ((((uint32)ipc->function -
				  (uint32)curpos)>>2)-2) & 0xFFFFFF));
    }
    ipc++;
    if (*(int *)ipc) {
      compile_w(0xE2866000 + sizeof(t_ipc)); /* ADD r6,r6,#sizeof(t_ipc) */
      compile_inctimer();
    }
  }
  compile_finalregs();
  compile_w(0xE8FD8070); /* LDMIA r13!,{r4-r6,PC}^ */
  compile_w(list->pc);
  p = realloc(block, curpos - block);
  if (p != block) {
    fprintf(stderr, "realloc moved block!\n");
    exit(1);
  }
  *(uint32 *)0x7000 = (uint32)block;

  /*
  sprintf(tmp, "b/%02x/blk%d", blockno/77, blockno++ % 77);
  file = fopen(tmp, "w");
  fwrite(block, 1, curpos - block, file);
  fclose(file);
  */

  r[0] = (uint32)1;
  r[1] = (uint32)block;
  r[2] = (uint32)(curpos-4);
  os_swi(0x20000 | OS_SynchroniseCodeAreas, r);

  return block;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.