Browse Source

32x: drc: new debug facility (pdb). Few fixes thanks to it.

git-svn-id: file:///home/notaz/opt/svn/PicoDrive@863 be3aeb3a-fb24-0410-a615-afba39da0efa
notaz 14 years ago
parent
commit
5686d93123

+ 407 - 0
cpu/debug.c

@@ -0,0 +1,407 @@
+/*
+ * vim:shiftwidth=2:expandtab
+ * PDB, the PicoDrive debugger
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+
+#include "../pico/pico_int.h"
+#include "debug.h"
+
+static char pdb_pending_cmds[128];
+static char pdb_event_cmds[128];
+
+static struct pdb_cpu {
+  void *context;
+  int type;
+  int id;
+  const char *name;
+  unsigned int bpts[16];
+  int bpt_count;
+  int icount;
+} pdb_cpus[5];
+static int pdb_cpu_count;
+
+static int pdb_global_icount;
+
+#ifdef PDB_NET
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+
+#include "debug_net.h"
+
+static int pdb_net_sock = -1;
+
+int pdb_net_connect(const char *host, const char *port)
+{
+  struct sockaddr_in sockadr;
+  int sock = -1;
+  int ret;
+
+  sock = socket(PF_INET, SOCK_STREAM, 0);
+  if (sock == -1) {
+    perror("socket");
+    return -1;
+  }
+
+  sockadr.sin_addr.s_addr = inet_addr(host);
+  sockadr.sin_family = AF_INET;
+  sockadr.sin_port = htons(atoi(port));
+
+  ret = connect(sock, (struct sockaddr *)&sockadr, sizeof(sockadr));
+  if (ret != 0) {
+    perror("pdb_net: connect");
+    close(sock);
+    return -1;
+  }
+
+  printf("pdb_net: connected to %s:%s\n", host, port);
+
+  pdb_net_sock = sock;
+  return 0;
+}
+
+static int pdb_net_send(struct pdb_cpu *cpu, unsigned int pc)
+{
+  packet_t packet;
+  int ret;
+
+  if (pdb_net_sock < 0)
+    return 0; // not connected
+
+  if (cpu->type == PDBCT_SH2) {
+    SH2 *sh2 = cpu->context;
+    int rl = offsetof(SH2, macl) + sizeof(sh2->macl);
+    packet.header.type = PDBCT_SH2;
+    packet.header.cpuid = cpu->id;
+    packet.regs[0] = pc;
+    memcpy(&packet.regs[1], sh2->r, rl);
+    packet.regs[1+24+0] = sh2->pdb_io_csum[0];
+    packet.regs[1+24+1] = sh2->pdb_io_csum[1];
+    packet.header.len = 4 + rl + 4*2;
+    sh2->pdb_io_csum[0] = sh2->pdb_io_csum[1] = 0;
+  }
+
+  ret = send(pdb_net_sock, &packet, sizeof(packet.header) + packet.header.len, MSG_NOSIGNAL);
+  if (ret != sizeof(packet.header) + packet.header.len) {
+    if (ret < 0)
+      perror("send");
+    else
+      printf("send: %d/%d\n", ret, sizeof(packet.header) + packet.header.len);
+    close(pdb_net_sock);
+    pdb_net_sock = -1;
+    ret = -1;
+  }
+  return ret;
+}
+#else
+#define pdb_net_send(a,b) 0
+#endif // PDB_NET
+
+#ifdef HAVE_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+static char *my_readline(const char *prompt)
+{
+  char *line = NULL;
+
+#ifdef HAVE_READLINE
+  line = readline("(pdb) ");
+  if (line == NULL)
+    return NULL;
+  if (line[0] != 0)
+    add_history(line);
+#else
+  size_t size = 0;
+  ssize_t ret;
+
+  printf("(pdb) ");
+  fflush(stdout);
+  ret = getline(&line, &size, stdin);
+  if (ret < 0)
+    return NULL;
+  if (ret > 0 && line[ret - 1] == '\n')
+    line[ret - 1] = 0;
+#endif
+
+  return line;
+}
+
+static struct pdb_cpu *context2cpu(const void *context)
+{
+  int i;
+  for (i = 0; i < pdb_cpu_count; i++)
+    if (pdb_cpus[i].context == context)
+      return &pdb_cpus[i];
+  return NULL;
+}
+
+static const char *get_token(char *buf, int blen, const char *str)
+{
+  const char *p, *s, *e;
+  int len;
+
+  p = str;
+  while (isspace_(*p))
+    p++;
+  if (*p == 0)
+    return NULL;
+  if (*p == ';') {
+    strcpy(buf, ";");
+    return p + 1;
+  }
+
+  s = p;
+  while (*p != 0 && *p != ';' && !isspace_(*p))
+    p++;
+  e = p;
+  while (isspace_(*e))
+    e++;
+
+  len = p - s;
+  if (len > blen - 1)
+    len = blen - 1;
+  memcpy(buf, s, len);
+  buf[len] = 0;
+  return e;
+}
+
+static const char *get_arg(char *buf, int blen, const char *str)
+{
+  if (*str == ';')
+    return NULL;
+  return get_token(buf, blen, str);
+}
+
+enum cmd_ret_e {
+  CMDRET_DONE,          // ..and back to prompt
+//  CMDRET_PROMPT,        // go to prompt
+  CMDRET_CONT_DO_NEXT,  // continue and do remaining cmds on next event
+  CMDRET_CONT_REDO,     // continue and redo all cmds on next event
+};
+
+static int do_print(struct pdb_cpu *cpu, const char *args)
+{
+  int i;
+  elprintf(EL_STATUS, "cpu %d (%s)", cpu->id, cpu->name);
+  if (cpu->type == PDBCT_SH2) {
+    SH2 *sh2 = cpu->context;
+    printf("PC,SR %08x,     %03x\n", sh2->pc, sh2->sr & 0x3ff);
+    for (i = 0; i < 16/2; i++)
+      printf("R%d,%2d %08x,%08x\n", i, i + 8, sh2->r[i], sh2->r[i + 8]);
+    printf("gb,vb %08x,%08x\n", sh2->gbr, sh2->vbr);
+    printf("IRQs/mask:        %02x/%02x\n", Pico32x.sh2irqi[sh2->is_slave],
+      Pico32x.sh2irq_mask[sh2->is_slave]);
+    printf("cycles %d/%d (%d)\n", sh2->cycles_done, sh2->cycles_aim, (signed int)sh2->sr >> 12);
+  }
+  return CMDRET_DONE;
+}
+
+static int do_step_all(struct pdb_cpu *cpu, const char *args)
+{
+  char tmp[32];
+  if (!get_arg(tmp, sizeof(tmp), args)) {
+    printf("step_all: missing arg\n");
+    return CMDRET_DONE;
+  }
+
+  pdb_global_icount = atoi(tmp);
+  return CMDRET_CONT_DO_NEXT;
+}
+
+static int do_continue(struct pdb_cpu *cpu, const char *args)
+{
+  char tmp[32];
+  if (get_arg(tmp, sizeof(tmp), args))
+    cpu->icount = atoi(tmp);
+  return CMDRET_CONT_DO_NEXT;
+}
+
+static int do_step(struct pdb_cpu *cpu, const char *args)
+{
+  cpu->icount = 1;
+  return do_continue(cpu, args);
+}
+
+static int do_waitcpu(struct pdb_cpu *cpu, const char *args)
+{
+  char tmp[32];
+  if (!get_arg(tmp, sizeof(tmp), args)) {
+    printf("waitcpu: missing arg\n");
+    return CMDRET_DONE;
+  }
+  if (strcmp(tmp, cpu->name) == 0)
+    return CMDRET_DONE;
+
+  return CMDRET_CONT_REDO;
+}
+
+static int do_help(struct pdb_cpu *cpu, const char *args);
+
+static struct {
+  const char *cmd;
+  const char *help;
+  int (*handler)(struct pdb_cpu *cpu, const char *args);
+} pdb_cmds[] = {
+  { "help",     "",          do_help },
+  { "continue", "[insns]",   do_continue },
+  { "step",     "[insns]",   do_step },
+  { "step_all", "<insns>",   do_step_all },
+  { "waitcpu",  "<cpuname>", do_waitcpu },
+  { "print",    "",          do_print },
+};
+
+static int do_help(struct pdb_cpu *cpu, const char *args)
+{
+  int i;
+  for (i = 0; i < ARRAY_SIZE(pdb_cmds); i++)
+    printf("%s %s\n", pdb_cmds[i].cmd, pdb_cmds[i].help);
+  return CMDRET_DONE;
+}
+
+static int do_comands(struct pdb_cpu *cpu, const char *cmds)
+{
+  const char *p = cmds;
+  while (p != NULL)
+  {
+    const char *pcmd;
+    char cmd[32];
+    int i, len;
+    int ret = 0;
+
+    pcmd = p;
+    p = get_token(cmd, sizeof(cmd), p);
+    if (p == NULL)
+      break;
+    if (cmd[0] == ';')
+      continue;
+
+    len = strlen(cmd);
+    for (i = 0; i < ARRAY_SIZE(pdb_cmds); i++)
+      if (strncmp(pdb_cmds[i].cmd, cmd, len) == 0) {
+        ret = pdb_cmds[i].handler(cpu, p);
+        break;
+      }
+
+    if (i == ARRAY_SIZE(pdb_cmds)) {
+      printf("bad cmd: %s\n", cmd);
+      break;
+    }
+
+    // skip until next command
+    while (1) {
+      p = get_token(cmd, sizeof(cmd), p);
+      if (p == NULL || cmd[0] == ';')
+        break;
+    }
+
+    pdb_event_cmds[0] = 0;
+    if (ret == CMDRET_CONT_DO_NEXT) {
+      pdb_pending_cmds[0] = 0;
+      if (p != NULL)
+        strcpy(pdb_event_cmds, p);
+      return 0;
+    }
+    if (ret == CMDRET_CONT_REDO) {
+      if (pcmd != pdb_pending_cmds)
+        strncpy(pdb_pending_cmds, pcmd, sizeof(pdb_pending_cmds));
+      return 0;
+    }
+    pdb_pending_cmds[0] = 0;
+  }
+
+  return 1;
+}
+
+static void do_prompt(struct pdb_cpu *cpu)
+{
+  static char prev[128];
+  int ret;
+
+  while (1) {
+    char *line, *cline;
+
+    line = my_readline("(pdb) ");
+    if (line == NULL)
+      break;
+    if (line[0] == 0)
+      cline = prev;
+    else {
+      cline = line;
+      strncpy(prev, line, sizeof(prev));
+    }
+      
+    ret = do_comands(cpu, cline);
+    free(line);
+
+    if (ret == 0)
+      break;
+  }
+}
+
+void pdb_register_cpu(void *context, int type, const char *name)
+{
+  int i = pdb_cpu_count;
+  memset(&pdb_cpus[i], 0, sizeof(pdb_cpus[i]));
+  pdb_cpus[i].context = context;
+  pdb_cpus[i].type = type;
+  pdb_cpus[i].id = pdb_cpu_count;
+  pdb_cpus[i].name = name;
+  pdb_cpus[i].icount = -1;
+  pdb_cpu_count++;
+}
+
+void pdb_step(void *context, unsigned int pc)
+{
+  struct pdb_cpu *cpu = context2cpu(context);
+  int i;
+
+  if (pdb_net_send(cpu, pc) < 0)
+    goto prompt;
+
+  if (pdb_pending_cmds[0] != 0)
+    if (do_comands(cpu, pdb_pending_cmds))
+      goto prompt;
+
+  // breakpoint?
+  for (i = 0; i < cpu->bpt_count; i++)
+    if (cpu->bpts[i] == pc)
+      goto prompt;
+
+  // hit num of insns?
+  if (pdb_global_icount > 0)
+    if (--pdb_global_icount == 0)
+      goto prompt;
+
+  if (cpu->icount > 0)
+    if (--(cpu->icount) == 0)
+      goto prompt;
+
+  return;
+
+prompt:
+  if (pdb_event_cmds[0] != 0)
+    if (!do_comands(cpu, pdb_event_cmds))
+      return;
+
+  printf("%s @%08x\n", cpu->name, pc);
+  do_prompt(cpu);
+}
+
+void pdb_command(const char *cmd)
+{
+  strncpy(pdb_pending_cmds, cmd, sizeof(pdb_pending_cmds));
+  pdb_pending_cmds[sizeof(pdb_pending_cmds) - 1] = 0;
+}
+
+void pdb_cleanup(void)
+{
+  pdb_cpu_count = 0;
+}
+

+ 31 - 0
cpu/debug.h

@@ -0,0 +1,31 @@
+
+#ifdef PDB
+
+enum {
+  PDBCT_SH2,
+};
+
+void pdb_register_cpu(void *context, int type, const char *name);
+void pdb_cleanup(void);
+void pdb_step(void *context, unsigned int pc);
+void pdb_command(const char *cmd);
+
+#else
+
+#define pdb_register_cpu(a,b,c)
+#define pdb_cleanup()
+#define pdb_step(a,b)
+#define pdb_command(a)
+
+#endif
+
+
+#if defined(PDB) && defined(PDB_NET)
+
+int pdb_net_connect(const char *host, const char *port);
+
+#else
+
+#define pdb_net_connect(a,b) 0
+
+#endif

+ 126 - 0
cpu/debug_net.c

@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include "debug_net.h"
+
+static const char * const regnames[] = {
+  "PC",
+  "R0",   "R1",   "R2",  "R3",  "R4",  "R5",  "R6",   "R7",
+  "R8",   "R9",   "R10", "R11", "R12", "R13", "R14",  "SP",
+  "PC2",  "PC3",  "PR",  "SR",  "GBR", "VBR", "MACH", "MACL",
+  "MEM0", "MEM1", // mem i/o csums
+};
+
+int main(int argc, char *argv[])
+{
+  unsigned int pc_trace[5][4], pc_trace_p[5] = { 0, };
+  struct addrinfo *ai, *ais, hints;
+  int sock = -1, sock1, sock2;
+  struct sockaddr_in6 sa;
+  packet_t packet1, packet2;
+  int i, ret, cnt, cpuid;
+  socklen_t sal;
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_flags = AI_CANONNAME;
+  hints.ai_family = PF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+
+  ret = getaddrinfo("::", "1234", &hints, &ais);
+  if (ret != 0)
+    return -1;
+
+  for (ai = ais; ai != NULL; ai = ai->ai_next) {
+    sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+    if (sock == -1)
+      continue;
+
+    ret = bind(sock, ai->ai_addr, ai->ai_addrlen);
+    if (ret != 0) {
+      close(sock);
+      sock = -1;
+      continue;
+    }
+    break;
+  }
+  freeaddrinfo(ais);
+
+  if (sock == -1) {
+    perror("failed to bind");
+    return -1;
+  }
+ 
+  ret = listen(sock, SOMAXCONN);
+  if (ret != 0) {
+    perror("failed to listen");
+    return -1;
+  }
+
+  sal = sizeof(sa);
+  sock1 = accept(sock, (struct sockaddr *)&sa, &sal);
+  if (sock1 == -1) {
+    perror("failed to accept");
+    return -1;
+  }
+  printf("client1 connected\n");
+
+  sal = sizeof(sa);
+  sock2 = accept(sock, (struct sockaddr *)&sa, &sal);
+  if (sock2 == -1) {
+    perror("failed to accept");
+    return -1;
+  }
+  printf("client2 connected\n");
+
+  for (cnt = 0; ; cnt++)
+  {
+#define tmp_size (4+4 + 24*4 + 2*4)
+    ret = recv(sock1, &packet1, tmp_size, MSG_WAITALL);
+    if (ret != tmp_size) {
+      if (ret < 0)
+        perror("recv1");
+      else
+        printf("recv1 %d\n", ret);
+      return -1;
+    }
+    ret = recv(sock2, &packet2, tmp_size, MSG_WAITALL);
+    if (ret != tmp_size) {
+      if (ret < 0)
+        perror("recv2");
+      else
+        printf("recv2 %d\n", ret);
+      return -1;
+    }
+
+    cpuid = packet1.header.cpuid;
+    if (memcmp(&packet1, &packet2, sizeof(packet1.header) + packet1.header.len) == 0) {
+      pc_trace[cpuid][pc_trace_p[cpuid]++ & 3] = packet1.regs[0];
+      continue;
+    }
+
+    if (*(int *)&packet1.header != *(int *)&packet2.header)
+      printf("%d: header\n", cnt);
+
+    // check regs (and stuff)
+    for (i = 0; i < 1+24+2; i++)
+      if (packet1.regs[i] != packet2.regs[i])
+        printf("%d: %3s: %08x %08x\n", cnt, regnames[i], packet1.regs[i], packet2.regs[i]);
+
+    break;
+  }
+
+  printf("--\nCPU %d, trace:", cpuid);
+  for (i = 0; i < 4; i++)
+    printf(" %08x", pc_trace[cpuid][pc_trace_p[cpuid]++ & 3]);
+  printf(" %08x\n", packet1.regs[0]);
+
+  for (i = 0; i < 24+1; i++)
+    printf("%3s: %08x %08x\n", regnames[i], packet1.regs[i], packet2.regs[i]);
+
+  return 0;
+}
+

+ 9 - 0
cpu/debug_net.h

@@ -0,0 +1,9 @@
+typedef struct {
+  struct {
+    unsigned char type;
+    unsigned char cpuid;
+    unsigned short len;
+  } header;
+  unsigned int regs[32];
+} packet_t;
+

+ 24 - 6
cpu/drc/emit_arm.c

@@ -241,30 +241,45 @@
 #define EOP_MSR_REG(rm)       EOP_C_MSR_REG(A_COND_AL,rm)
 
 
-// XXX: AND, RSB, *C, MVN will break if 1 insn is not enough
+// XXX: AND, RSB, *C, will break if 1 insn is not enough
 static void emith_op_imm2(int cond, int s, int op, int rd, int rn, unsigned int imm)
 {
 	int ror2;
 	u32 v;
 
-	if (op == A_OP_MOV) {
+	switch (op) {
+	case A_OP_MOV:
 		rn = 0;
-		if (~imm < 0x100) {
+		if (~imm < 0x10000) {
 			imm = ~imm;
 			op = A_OP_MVN;
 		}
-	} else if (imm == 0)
-		return;
+		break;
+
+	case A_OP_EOR:
+	case A_OP_SUB:
+	case A_OP_ADD:
+	case A_OP_ORR:
+	case A_OP_BIC:
+		if (s == 0 && imm == 0)
+			return;
+		break;
+	}
 
-	for (v = imm, ror2 = 0; v != 0 || op == A_OP_MOV; v >>= 8, ror2 -= 8/2) {
+	for (v = imm, ror2 = 0; ; ror2 -= 8/2) {
 		/* shift down to get 'best' rot2 */
 		for (; v && !(v & 3); v >>= 2)
 			ror2--;
 
 		EOP_C_DOP_IMM(cond, op, s, rn, rd, ror2 & 0x0f, v & 0xff);
 
+		v >>= 8;
+		if (v == 0)
+			break;
 		if (op == A_OP_MOV)
 			op = A_OP_ORR;
+		if (op == A_OP_MVN)
+			op = A_OP_BIC;
 		rn = rd;
 	}
 }
@@ -414,6 +429,9 @@ static int emith_xbranch(int cond, void *target, int is_call)
 #define emith_add_r_imm(r, imm) \
 	emith_op_imm(A_COND_AL, 0, A_OP_ADD, r, imm)
 
+#define emith_adc_r_imm(r, imm) \
+	emith_op_imm(A_COND_AL, 0, A_OP_ADC, r, imm)
+
 #define emith_sub_r_imm(r, imm) \
 	emith_op_imm(A_COND_AL, 0, A_OP_SUB, r, imm)
 

+ 6 - 1
cpu/drc/emit_x86.c

@@ -180,13 +180,18 @@ enum { xAX = 0, xCX, xDX, xBX, xSP, xBP, xSI, xDI };
 	EMIT(imm, u32); \
 } while (0)
 
-// 2 - adc, 3 - sbb
 #define emith_add_r_imm(r, imm) \
 	emith_arith_r_imm(0, r, imm)
 
 #define emith_or_r_imm(r, imm) \
 	emith_arith_r_imm(1, r, imm)
 
+#define emith_adc_r_imm(r, imm) \
+	emith_arith_r_imm(2, r, imm)
+
+#define emith_sbc_r_imm(r, imm) \
+	emith_arith_r_imm(3, r, imm) // sbb
+
 #define emith_and_r_imm(r, imm) \
 	emith_arith_r_imm(4, r, imm)
 

+ 78 - 20
cpu/sh2/compiler.c

@@ -29,6 +29,7 @@
 #include "sh2.h"
 #include "compiler.h"
 #include "../drc/cmn.h"
+#include "../debug.h"
 
 // features
 #define PROPAGATE_CONSTANTS     1
@@ -68,12 +69,14 @@ static char sh2dasm_buff[64];
 #define do_host_disasm(x)
 #endif
 
-#if (DRC_DEBUG & 4)
-static void REGPARM(3) *sh2_drc_announce_entry(void *block, SH2 *sh2, u32 sr)
+#if (DRC_DEBUG & 4) || defined(PDB)
+static void REGPARM(3) *sh2_drc_log_entry(void *block, SH2 *sh2, u32 sr)
 {
-  if (block != NULL)
+  if (block != NULL) {
     dbg(4, "= %csh2 enter %08x %p, c=%d", sh2->is_slave ? 's' : 'm',
       sh2->pc, block, (signed int)sr >> 12);
+    pdb_step(sh2, sh2->pc);
+  }
   return block;
 }
 #endif
@@ -216,10 +219,15 @@ static void REGPARM(1) (*sh2_drc_entry)(SH2 *sh2);
 static void            (*sh2_drc_dispatcher)(void);
 static void            (*sh2_drc_exit)(void);
 static void            (*sh2_drc_test_irq)(void);
+
+static u32  REGPARM(2) (*sh2_drc_read8)(u32 a, SH2 *sh2);
+static u32  REGPARM(2) (*sh2_drc_read16)(u32 a, SH2 *sh2);
+static u32  REGPARM(2) (*sh2_drc_read32)(u32 a, SH2 *sh2);
 static void REGPARM(2) (*sh2_drc_write8)(u32 a, u32 d);
 static void REGPARM(2) (*sh2_drc_write8_slot)(u32 a, u32 d);
 static void REGPARM(2) (*sh2_drc_write16)(u32 a, u32 d);
 static void REGPARM(2) (*sh2_drc_write16_slot)(u32 a, u32 d);
+static int  REGPARM(3) (*sh2_drc_write32)(u32 a, u32 d, SH2 *sh2);
 
 extern void REGPARM(2) sh2_do_op(SH2 *sh2, int opcode);
 
@@ -242,6 +250,7 @@ static void flush_tcache(int tcid)
 #endif
 }
 
+#if LINK_BRANCHES
 // add block links (tracked branches)
 static int dr_add_block_link(u32 target_pc, void *jump, int tcache_id)
 {
@@ -259,6 +268,7 @@ static int dr_add_block_link(u32 target_pc, void *jump, int tcache_id)
 
   return 0;
 }
+#endif
 
 static void *dr_find_block(block_desc *tab, u32 addr)
 {
@@ -848,7 +858,7 @@ static int emit_memhandler_read_(int size, int ram_check)
   arg1 = rcache_get_tmp_arg(1);
   emith_move_r_r(arg1, CONTEXT_REG);
 
-#if 1
+#ifndef PDB_NET
   if (ram_check && Pico.rom == (void *)0x02000000 && Pico32xMem->sdram == (void *)0x06000000) {
     int tmp = rcache_get_tmp();
     emith_and_r_r_imm(tmp, arg0, 0xfb000000);
@@ -859,14 +869,14 @@ static int emit_memhandler_read_(int size, int ram_check)
       emith_eor_r_imm_c(DCOND_EQ, arg0, 1);
       emith_read8_r_r_offs_c(DCOND_EQ, arg0, arg0, 0);
       EMITH_SJMP3_MID(DCOND_NE);
-      emith_call_cond(DCOND_NE, p32x_sh2_read8);
+      emith_call_cond(DCOND_NE, sh2_drc_read8);
       EMITH_SJMP3_END();
       break;
     case 1: // 16
       EMITH_SJMP3_START(DCOND_NE);
       emith_read16_r_r_offs_c(DCOND_EQ, arg0, arg0, 0);
       EMITH_SJMP3_MID(DCOND_NE);
-      emith_call_cond(DCOND_NE, p32x_sh2_read16);
+      emith_call_cond(DCOND_NE, sh2_drc_read16);
       EMITH_SJMP3_END();
       break;
     case 2: // 32
@@ -874,7 +884,7 @@ static int emit_memhandler_read_(int size, int ram_check)
       emith_read_r_r_offs_c(DCOND_EQ, arg0, arg0, 0);
       emith_ror_c(DCOND_EQ, arg0, arg0, 16);
       EMITH_SJMP3_MID(DCOND_NE);
-      emith_call_cond(DCOND_NE, p32x_sh2_read32);
+      emith_call_cond(DCOND_NE, sh2_drc_read32);
       EMITH_SJMP3_END();
       break;
     }
@@ -884,13 +894,13 @@ static int emit_memhandler_read_(int size, int ram_check)
   {
     switch (size) {
     case 0: // 8
-      emith_call(p32x_sh2_read8);
+      emith_call(sh2_drc_read8);
       break;
     case 1: // 16
-      emith_call(p32x_sh2_read16);
+      emith_call(sh2_drc_read16);
       break;
     case 2: // 32
-      emith_call(p32x_sh2_read32);
+      emith_call(sh2_drc_read32);
       break;
     }
   }
@@ -974,7 +984,7 @@ static void emit_memhandler_write(int size, u32 pc, int delay)
     break;
   case 2: // 32
     emith_move_r_r(ctxr, CONTEXT_REG);
-    emith_call(p32x_sh2_write32);
+    emith_call(sh2_drc_write32);
     break;
   }
   rcache_invalidate();
@@ -1050,10 +1060,11 @@ static void emit_block_entry(void)
   host_arg2reg(arg1, 1);
   host_arg2reg(arg2, 2);
 
-#if (DRC_DEBUG & 4)
+#if (DRC_DEBUG & 4) || defined(PDB)
+  emit_do_static_regs(1, arg2);
   emith_move_r_r(arg1, CONTEXT_REG);
   emith_move_r_r(arg2, rcache_get_reg(SHR_SR, RC_GR_READ));
-  emith_call(sh2_drc_announce_entry);
+  emith_call(sh2_drc_log_entry);
   rcache_invalidate();
 #endif
   emith_tst_r_r(arg0, arg0);
@@ -1256,10 +1267,6 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
   }
   branch_target_count = tmp;
   memset(branch_target_ptr, 0, sizeof(branch_target_ptr[0]) * branch_target_count);
-#if !LINK_BRANCHES
-  // for debug
-  branch_target_count = 0;
-#endif
 
   // -------------------------------------------------
   // 2nd pass: actual compilation
@@ -2479,6 +2486,7 @@ end_op:
       else
         emith_tst_r_imm(sr, T);
 
+#if LINK_BRANCHES
       if (find_in_array(branch_target_pc, branch_target_count, target_pc) >= 0) {
         // local branch
         // XXX: jumps back can be linked already
@@ -2492,7 +2500,9 @@ end_op:
           break;
         }
       }
-      else {
+      else
+#endif
+      {
         // can't resolve branch locally, make a block exit
         emit_move_r_imm32(SHR_PC, target_pc);
         rcache_clean();
@@ -2622,6 +2632,11 @@ static void sh2_generate_utils(void)
   int arg0, arg1, arg2, sr, tmp;
   void *sh2_drc_write_end, *sh2_drc_write_slot_end;
 
+  sh2_drc_write32 = p32x_sh2_write32;
+  sh2_drc_read8  = p32x_sh2_read8;
+  sh2_drc_read16 = p32x_sh2_read16;
+  sh2_drc_read32 = p32x_sh2_read32;
+
   host_arg2reg(arg0, 0);
   host_arg2reg(arg1, 1);
   host_arg2reg(arg2, 2);
@@ -2679,7 +2694,7 @@ static void sh2_generate_utils(void)
   tmp = rcache_get_reg_arg(1, SHR_SR);
   emith_clear_msb(tmp, tmp, 22);
   emith_move_r_r(arg2, CONTEXT_REG);
-  emith_call(p32x_sh2_write32);
+  emith_call(p32x_sh2_write32); // XXX: use sh2_drc_write32?
   rcache_invalidate();
   // push PC
   rcache_get_reg_arg(0, SHR_SP);
@@ -2722,7 +2737,6 @@ static void sh2_generate_utils(void)
   EMITH_SJMP_START(DCOND_NE);
   emith_jump_ctx_c(DCOND_EQ, offsetof(SH2, drc_tmp)); // return
   EMITH_SJMP_END(DCOND_NE);
-  // since PC is up to date, jump to it's block instead of returning
   emith_call(sh2_drc_test_irq);
   emith_jump_ctx(offsetof(SH2, drc_tmp));
 
@@ -2763,6 +2777,50 @@ static void sh2_generate_utils(void)
   emith_ctx_read(arg2, offsetof(SH2, write16_tab));
   emith_sh2_wcall(arg0, arg2, sh2_drc_write_slot_end);
 
+#ifdef PDB_NET
+  // debug
+  #define MAKE_READ_WRAPPER(func) { \
+    void *tmp = (void *)tcache_ptr; \
+    emith_ret_to_ctx(offsetof(SH2, drc_tmp)); \
+    emith_call(func); \
+    emith_ctx_read(arg2, offsetof(SH2, pdb_io_csum[0]));  \
+    emith_addf_r_r(arg2, arg0);                           \
+    emith_ctx_write(arg2, offsetof(SH2, pdb_io_csum[0])); \
+    emith_ctx_read(arg2, offsetof(SH2, pdb_io_csum[1]));  \
+    emith_adc_r_imm(arg2, 0x01000000);                    \
+    emith_ctx_write(arg2, offsetof(SH2, pdb_io_csum[1])); \
+    emith_jump_ctx(offsetof(SH2, drc_tmp)); \
+    func = tmp; \
+  }
+  #define MAKE_WRITE_WRAPPER(func) { \
+    void *tmp = (void *)tcache_ptr; \
+    emith_ctx_read(arg2, offsetof(SH2, pdb_io_csum[0]));  \
+    emith_addf_r_r(arg2, arg1);                           \
+    emith_ctx_write(arg2, offsetof(SH2, pdb_io_csum[0])); \
+    emith_ctx_read(arg2, offsetof(SH2, pdb_io_csum[1]));  \
+    emith_adc_r_imm(arg2, 0x01000000);                    \
+    emith_ctx_write(arg2, offsetof(SH2, pdb_io_csum[1])); \
+    emith_move_r_r(arg2, CONTEXT_REG);                    \
+    emith_jump(func); \
+    func = tmp; \
+  }
+
+  MAKE_READ_WRAPPER(sh2_drc_read8);
+  MAKE_READ_WRAPPER(sh2_drc_read16);
+  MAKE_READ_WRAPPER(sh2_drc_read32);
+  MAKE_WRITE_WRAPPER(sh2_drc_write8);
+  MAKE_WRITE_WRAPPER(sh2_drc_write8_slot);
+  MAKE_WRITE_WRAPPER(sh2_drc_write16);
+  MAKE_WRITE_WRAPPER(sh2_drc_write16_slot);
+  MAKE_WRITE_WRAPPER(sh2_drc_write32);
+#if (DRC_DEBUG & 2)
+  host_dasm_new_symbol(sh2_drc_read8);
+  host_dasm_new_symbol(sh2_drc_read16);
+  host_dasm_new_symbol(sh2_drc_read32);
+  host_dasm_new_symbol(sh2_drc_write32);
+#endif
+#endif
+
   rcache_invalidate();
 #if (DRC_DEBUG & 2)
   host_dasm_new_symbol(sh2_drc_entry);

+ 2 - 0
cpu/sh2/sh2.c

@@ -1,5 +1,6 @@
 #include <string.h>
 #include "sh2.h"
+#include "../debug.h"
 #include "compiler.h"
 
 #define I 0xf0
@@ -12,6 +13,7 @@ int sh2_init(SH2 *sh2, int is_slave)
 
 	memset(sh2, 0, sizeof(*sh2));
 	sh2->is_slave = is_slave;
+	pdb_register_cpu(sh2, PDBCT_SH2, is_slave ? "ssh2" : "msh2");
 #ifdef DRC_SH2
 	ret = sh2_drc_init(sh2);
 #endif

+ 1 - 0
cpu/sh2/sh2.h

@@ -37,6 +37,7 @@ typedef struct SH2_
 	void		*p_da;
 	void		*p_sdram;
 	void		*p_rom;
+	unsigned int	pdb_io_csum[2];
 
 	// interpreter stuff
 	int		icount;		// cycles left in current timeslice

+ 11 - 0
platform/common/common.mak

@@ -7,6 +7,17 @@ endif
 ifeq "$(profile)" "2"
 CFLAGS += -fprofile-use
 endif
+ifeq "$(pdb)" "1"
+DEFINES += PDB
+OBJS += cpu/debug.o
+ ifeq "$(pdb_net)" "1"
+ DEFINES += PDB_NET
+ endif
+ ifeq "$(readline)" "1"
+ DEFINES += HAVE_READLINE
+ LDFLAGS += -lreadline
+ endif
+endif
 ifeq "$(pprof)" "1"
 DEFINES += PPROF
 OBJS += platform/linux/pprof.o

+ 7 - 0
platform/common/main.c

@@ -13,6 +13,7 @@
 #include "config.h"
 #include "input.h"
 #include "plat.h"
+#include <cpu/debug.h>
 #include <version.h>
 
 
@@ -34,6 +35,12 @@ void parse_cmd_line(int argc, char *argv[])
 			else if (strcasecmp(argv[x], "-loadstate") == 0) {
 				if (x+1 < argc) { ++x; load_state_slot = atoi(argv[x]); }
 			}
+			else if (strcasecmp(argv[x], "-pdb") == 0) {
+				if (x+1 < argc) { ++x; pdb_command(argv[x]); }
+			}
+			else if (strcasecmp(argv[x], "-pdb_connect") == 0) {
+				if (x+2 < argc) { pdb_net_connect(argv[x+1], argv[x+2]); x += 2; }
+			}
 			else {
 				unrecognized = 1;
 				break;

+ 3 - 3
platform/linux/Makefile

@@ -12,6 +12,8 @@ use_sh2drc = 1
 #drc_debug_interp = 1
 #profile = 1
 
+all: mkdirs PicoDrive
+
 -include Makefile.local
 
 ifndef ARCH
@@ -27,7 +29,7 @@ CFLAGS += -mcpu=arm920t
 DEFINES += ARM
 endif
 
-CC = $(CROSS)gcc
+CC ?= $(CROSS)gcc
 
 # frontend
 OBJS += io.o emu.o blit.o in_evdev.o plat.o sndout_oss.o log_io.o
@@ -70,8 +72,6 @@ vpath %.asm = ../..
 
 DIRS += platform/linux zlib unzip
 
-all: mkdirs PicoDrive
-
 include ../common/common.mak
 include ../common/revision.mak
 

+ 1 - 1
platform/linux/host_dasm.c

@@ -188,7 +188,7 @@ void host_dasm(void *addr, int len)
     if (name != NULL)
       printf("%s:\n", name);
 
-    printf("     %08lx ", (long)vma);
+    printf("   %08lx ", (long)vma);
     vma += print_insn_func(vma, &di);
     printf("\n");
   }

+ 2 - 2
platform/linux/port_config.h

@@ -23,8 +23,8 @@
 #define SIMPLE_WRITE_SOUND	0
 #define mix_32_to_16l_stereo_lvl mix_32_to_16l_stereo
 
-#define EL_LOGMASK (EL_STATUS|EL_ANOMALY|EL_UIO|EL_IDLE)
-// EL_VDPDMA|EL_ASVDP|EL_SR | EL_BUSREQ|EL_Z80BNK | EL_32X)
+#define EL_LOGMASK (EL_STATUS|EL_ANOMALY|EL_UIO)
+// EL_VDPDMA|EL_ASVDP|EL_SR | EL_IDLE | EL_BUSREQ|EL_Z80BNK | EL_32X)
 
 //#define dprintf(f,...) printf("%05i:%03i: " f "\n",Pico.m.frame_count,Pico.m.scanline,##__VA_ARGS__)
 #define dprintf(x...)