home *** CD-ROM | disk | FTP | other *** search
-
- /*
- ==========
- DEBUGGER.C
- ==========
- */
-
-
- #include <stdio.h>
-
- #include "v9t9_common.h"
- #include "9900.h"
- #include "9900st.h"
- #include "memory.h"
- #include "vdp.h"
- #include "grom.h"
- #include "speech.h"
- #include "system.h"
- #include "debugger.h"
-
- #define _L LOG_CPU
-
- static char *
- decR(char *buf, int val)
- {
- *buf++ = 'R';
- if (val >= 10) {
- *buf++ = '1';
- val -= 10;
- }
- *buf++ = (val + '0');
- return buf;
- }
-
- #define NO_DOMAIN (mem_domain)(-1)
-
- static Memory views[MEMORY_VIEW_COUNT];
- int debugger_memory_view_size[MEMORY_VIEW_COUNT] = { 16, 16, 16, 16, 16 };
- bool debugger_operand_view_verbose = true;
-
- static u16 register_view;
-
- static void
- instruction_decode(u16 op, u16 pc, u16 wp, u16 st,
- Instruction *ins);
-
- static u8
- MEMORY_READ_MM_BYTE(u16 x)
- {
- x &= 0x9c02;
-
- return x == 0x8800 ? domain_read_byte(md_video, vdp_mmio_get_addr()) :
- x == 0x8802 ? vdp_mmio_get_status() :
- x == 0x9000 ? domain_read_byte(md_speech, speech_mmio_get_addr()) :
- x == 0x9800 ? domain_read_byte(md_graphics, grom_mmio_get_addr()) :
- x == 0x9802 ? grom_mmio_get_addr_byte() :
- 0;
- }
-
- #define flatmem(x) (byteop ? flatmem8(x)&0xff : flatmem16(x)&0xffff)
- #define flatmem8(x) (((x)>=0x8400 && (x)<0xa000 ? MEMORY_READ_MM_BYTE(x) : memory_read_byte(x))&0xff)
- #define flatmem16(x) (((x)>=0x8400 && (x)<0xa000 ? MEMORY_READ_MM_BYTE(x)<<8 : memory_read_word(x))&0xffff)
-
- /*
- * Complete an operand by fixing up val and ea as needed.
- *
- * addr is the address of the PC.
- */
- static void
- operand_complete(Operand *op, u16 *addr)
- {
- switch (op->type)
- {
- case OP_NONE:
- break;
- case OP_REG: // Rx
- op->ea = (op->val<<1) + wp;
- break;
- case OP_IND: // *Rx
- case OP_INC: // *Rx+
- op->ea = flatmem16((op->val<<1) + wp);
- break;
- case OP_ADDR: // @>xxxx or @>xxxx(Rx)
- op->ea = op->immed = MEMORY_READ_WORD(*addr); *addr += 2;
- if (op->val != 0) {
- op->ea += flatmem16((op->val<<1) + wp);
- }
- break;
- case OP_IMMED: // immediate
- op->ea = *addr;
- op->immed = MEMORY_READ_WORD(*addr); *addr += 2;
- break;
- case OP_CNT: // shift count
- break;
- case OP_OFFS: // offset from R12
- op->ea = flatmem16((12<<1) + wp) + op->val;
- break;
- case OP_JUMP: // jump target
- op->val <<= 1; // byte -> word
- op->ea = op->val + *addr;
- break;
- case OP_STATUS: // status word
- break;
- case OP_INST:
- break; // can't handle here
- }
- }
-
- /*
- * Print out an operand into a disassembler operand
- */
- char *
- debugger_instruction_operand_print(Operand *op, char *buffer)
- {
- switch (op->type)
- {
- case OP_REG:
- sprintf(buffer, "R%d", op->val);
- break;
-
- case OP_IND:
- sprintf(buffer, "*R%d", op->val);
- break;
-
- case OP_ADDR:
- if (op->val == 0) {
- sprintf(buffer, "@>%04X", op->immed);
- } else {
- sprintf(buffer, "@>%04X(R%d)", op->immed, op->val);
- }
- break;
-
- case OP_INC:
- sprintf(buffer, "*R%d+", op->val);
- break;
-
- case OP_IMMED:
- sprintf(buffer, ">%04X", op->immed);
- break;
-
- case OP_CNT:
- sprintf(buffer, "%d", op->val);
- break;
-
- case OP_OFFS:
- sprintf(buffer, ">%s%02X",
- (op->val & 0x8000) ? "-" : "",
- (op->val & 0x8000) ? -op->val : op->val);
- break;
-
- case OP_JUMP:
- sprintf(buffer, "$+>%04X", op->val);
- break;
-
- case OP_STATUS: // not real operands
- case OP_INST:
- default:
- return 0L;
- }
-
- return buffer;
- }
-
- /*
- * Print value of operand to buffer
- *
- * verbose==true means to print extra info
- * after==true means this operand as the destination of
- * previous instruction
- */
- char *
- debugger_operand_value_print(Instruction *inst, Operand *op,
- bool verbose, bool after,
- char *buffer)
- {
- const char *equ = after ? ":=" : "=";
-
- // is operand not a destination?
- if (after && !op->dest)
- return NULL;
-
- // if source operand is killed, we don't care to see it
- if (!after && op->dest == OP_DEST_KILLED)
- return NULL;
-
- // ignore this operand?
- if (op->ignore)
- return NULL;
-
- switch (op->type)
- {
- case OP_REG:
- if (inst->opcode >= 0x3800 && inst->opcode < 0x3C00)
- {
- // MPY uses two adjacent registers
- if (after)
- if (verbose)
- sprintf(buffer, "R%d,R%d%s>%04X%04X",
- op->val, op->val+1, equ,
- flatmem16(op->ea),
- flatmem16(op->ea + 2));
- else
- sprintf(buffer, ">%04X%04X",
- flatmem16(op->ea),
- flatmem16(op->ea + 2));
- else
- if (verbose)
- sprintf(buffer, "R%d%s>%04X",
- op->val, equ, flatmem16(op->ea));
- else
- sprintf(buffer, ">%04X", flatmem16(op->ea));
- }
- else if (inst->opcode >= 0x3C00 && inst->opcode < 0x4000)
- {
- // DIV uses two adjacent registers
- if (!after)
- if (verbose)
- sprintf(buffer, "R%d,R%d%s>%04X%04X",
- op->val, op->val+1, equ,
- flatmem16(op->ea),
- flatmem16(op->ea + 2));
- else
- sprintf(buffer, ">%04X%04X",
- flatmem16(op->ea),
- flatmem16(op->ea + 2));
-
- else
- if (verbose)
- sprintf(buffer, "qR%d%s>%04X,rR%d%s>%04X",
- op->val, equ, flatmem16(op->ea),
- op->val+1, equ, flatmem16(op->ea + 2));
- else
- sprintf(buffer, "Q>%04X R>%04X",
- flatmem16(op->ea),
- flatmem16(op->ea + 2));
- }
- else
- {
- if (op->byteop)
- if (verbose)
- sprintf(buffer, "R%d%s>%02X",
- op->val, equ, flatmem8(op->ea));
- else
- sprintf(buffer, ">%02X", flatmem8(op->ea));
- else
- if (verbose)
- sprintf(buffer, "R%d%s>%04X",
- op->val, equ, flatmem16(op->ea));
- else
- sprintf(buffer, ">%04X", flatmem16(op->ea));
- }
- break;
-
- case OP_INC:
- case OP_IND:
- if (after)
- {
- if (op->byteop)
- if (verbose)
- sprintf(buffer, "R%d%s>%02X",
- op->val, equ, flatmem8(op->ea));
- else
- sprintf(buffer, ">%02X", flatmem8(op->ea));
- else
- if (verbose)
- sprintf(buffer, "R%d%s>%04X",
- op->val, equ, flatmem16(op->ea));
- else
- sprintf(buffer, ">%04X", flatmem16(op->ea));
- break;
- }
- // else show address
-
- case OP_ADDR:
- if (op->byteop)
- if (verbose)
- // if address points to a register, point this out
- if (op->ea >= inst->wp && op->ea < inst->wp+32)
- sprintf(buffer, "%c%d%s>%02X",
- (op->ea&1) ? 'r' : 'R', // low or high byte
- (op->ea - inst->wp)>>1,
- equ,
- flatmem8(op->ea));
- else
- sprintf(buffer, ">%04X%s>%02X",
- op->ea, equ,
- flatmem8(op->ea));
- else
- sprintf(buffer, ">%02X", flatmem8(op->ea));
- else
- if (verbose)
- // if address points to a register, point this out
- if (op->ea >= inst->wp && op->ea < inst->wp+32)
- sprintf(buffer, "R%d%s>%04X",
- (op->ea - inst->wp)>>1,
- equ,
- flatmem16(op->ea));
- else
- sprintf(buffer, ">%04X%s>%04X",
- op->ea, equ,
- flatmem16(op->ea));
- else
- sprintf(buffer, ">%04X", flatmem16(op->ea));
- break;
-
- /*
- case OP_IMMED:
- sprintf(buffer, ">%04X",op->immed);
- break;
-
- case OP_CNT:
- sprintf(buffer, ">%04X",op->val);
- break;
- */
-
- case OP_OFFS:
- case OP_JUMP:
- sprintf(buffer, ">%04X",op->ea);
- break;
-
- case OP_STATUS:
- sprintf(buffer, "<%s%s%s%s%s%s%s|%x>",
- (inst->status&ST_L) ? "L" : "",
- (inst->status&ST_A) ? "A" : "",
- (inst->status&ST_E) ? "E" : "",
- (inst->status&ST_C) ? "C" : "",
- (inst->status&ST_O) ? "O" : "",
- (inst->status&ST_P) ? "P" : "",
- (inst->status&ST_X) ? "X" : "",
- inst->status&ST_INTLEVEL);
- break;
-
- case OP_INST:
- {
- // sub-instruction!
- Instruction xinst;
- char op1[32], *op1ptr, op2[32], *op2ptr;
- instruction_decode(op->val,
- inst->pc, inst->wp, inst->status,
- &xinst);
- if (verbose)
- {
- op1ptr = debugger_instruction_operand_print(&xinst.op1, op1);
- op2ptr = debugger_instruction_operand_print(&xinst.op2, op2);
- sprintf(buffer, "(>%04X %s %s%s%s)",
- xinst.opcode,
- xinst.name,
- op1ptr ? op1ptr : "",
- op2ptr ? "," : "",
- op2ptr ? op2ptr : "");
- }
- else
- {
- sprintf(buffer, "(>%04X %s)",
- xinst.opcode,
- xinst.name);
- }
- }
- break;
-
- default:
- return 0L;
- }
-
- return buffer;
- }
-
-
- /*
- * Decode an instruction with opcode 'op' at 'addr'
- * into 'ins'
- */
- static void
- instruction_decode(u16 op, u16 pc, u16 wp, u16 st,
- Instruction *ins)
- {
- Instruction inst;
-
- memset(&inst, 0, sizeof(inst));
-
- inst.opcode = op;
- inst.name = "????";
- inst.op1.type = inst.op2.type = OP_NONE;
-
- inst.pc = pc;
- inst.wp = wp;
- inst.status = st;
-
- // Collect the instruction name
- // and operand structure.
-
- pc += 2; // point to operands
-
- // Initially, inst.op?.val is incomplete, and is whatever
- // raw data from the opcode we can decode;
- // inst.op?.ea is that of the instruction or immediate
- // if the operand needs it.
-
- // after decoding the instruction, we complete
- // the operand, making inst.op?.val and inst.op?.ea valid.
-
- if (op < 0x200) // data
- {
- inst.op1.type = OP_IMMED;
- pc -= 2; // instruction itself is value
- inst.name = "DATA";
-
- } else if (op < 0x2a0) {
- inst.op1.type = OP_REG;
- inst.op1.val = op & 15;
- inst.op1.dest = true;
- inst.op2.type = OP_IMMED;
- switch ((op & 0x1e0) >> 5)
- {
- case 0: inst.name = "LI ";
- inst.op1.dest = OP_DEST_KILLED;
- break;
- case 1: inst.name = "AI "; break;
- case 2: inst.name = "ANDI"; break;
- case 3: inst.name = "ORI "; break;
- case 4: inst.name = "CI ";
- inst.op1.dest = false; break;
- }
-
- } else if (op < 0x2e0) {
- inst.op1.type = OP_REG;
- inst.op1.val = op & 15;
- inst.op1.dest = OP_DEST_KILLED;
- switch ((op & 0x1e0) >> 5)
- {
- case 5: inst.name = "STWP"; break;
- case 6: inst.name = "STST";
- inst.op2.type = OP_STATUS;
- inst.op2.val = st;
- break;
- }
-
- } else if (op < 0x320) {
- inst.op1.type = OP_IMMED;
-
- switch ((op & 0x1e0) >> 5) {
- case 7: inst.name = "LWPI"; break;
- case 8: inst.name = "LIMI"; break;
- }
-
- } else if (op < 0x400) {
- switch ((op & 0x1e0) >> 5) {
- case 10: inst.name = "IDLE"; break;
- case 11: inst.name = "RSET"; break;
- case 12: inst.name = "RTWP";
- inst.op1.type = OP_STATUS;
- inst.op1.val = st;
- break;
- case 13: inst.name = "CKON"; break;
- case 14: inst.name = "CKOF"; break;
- case 15: inst.name = "LREX"; break;
- }
-
- } else if (op < 0x800) {
- inst.op1.type = (op & 0x30) >> 4;
- inst.op1.val = op & 15;
- inst.op1.dest = true;
-
- switch ((op & 0x3c0) >> 6)
- {
- case 0: inst.name = "BLWP";
- inst.op1.dest = false;
- inst.op1.ignore = true;
- break;
- case 1: inst.name = "B ";
- inst.op1.dest = false;
- inst.op1.ignore = true;
- break;
- case 2: inst.name = "X ";
- inst.op1.dest = false;
- inst.op2.type = OP_INST;
- break;
- case 3: inst.name = "CLR ";
- inst.op1.dest = OP_DEST_KILLED;
- break;
- case 4: inst.name = "NEG "; break;
- case 5: inst.name = "INV "; break;
- case 6: inst.name = "INC "; break;
- case 7: inst.name = "INCT"; break;
- case 8: inst.name = "DEC "; break;
- case 9: inst.name = "DECT"; break;
- case 10: inst.name = "BL ";
- inst.op1.dest = false;
- inst.op1.ignore = true;
- break;
- case 11: inst.name = "SWPB"; break;
- case 12: inst.name = "SETO";
- inst.op1.dest = OP_DEST_KILLED;
- break;
- case 13: inst.name = "ABS "; break;
- }
-
- } else if (op < 0xc00) {
- inst.op1.type = OP_REG;
- inst.op1.val = op & 15;
- inst.op1.dest = true;
- inst.op2.type = OP_CNT;
- inst.op2.val = (op & 0xf0) >> 4;
-
- // shift of zero comes from R0
- if (inst.op2.val == 0) {
- inst.op2.type = OP_REG;
- inst.op2.val = 0;
- }
-
- switch ((op & 0x700) >> 8)
- {
- case 0: inst.name = "SRA "; break;
- case 1: inst.name = "SRL "; break;
- case 2: inst.name = "SLA "; break;
- case 3: inst.name = "SRC "; break;
- }
-
- } else if (op < 0x1000) {
- switch ((op & 0x7e0) >> 5) {
- // !!! extended instructions
- }
-
- } else if (op < 0x2000) {
- if (op < 0x1d00) {
- inst.op1.type = OP_JUMP;
- inst.op1.val = ((s8) (op & 0xff));
- inst.op2.type = OP_STATUS;
- inst.op2.val = st;
- } else {
- inst.op1.type = OP_OFFS;
- inst.op1.val = ((s8) (op & 0xff));
- }
-
- switch ((op & 0xf00) >> 8) {
- case 0: inst.name = "JMP "; break;
- case 1: inst.name = "JLT "; break;
- case 2: inst.name = "JLE "; break;
- case 3: inst.name = "JEQ "; break;
- case 4: inst.name = "JHE "; break;
- case 5: inst.name = "JGT "; break;
- case 6: inst.name = "JNE "; break;
- case 7: inst.name = "JNC "; break;
- case 8: inst.name = "JOC "; break;
- case 9: inst.name = "JNO "; break;
- case 10: inst.name = "JL "; break;
- case 11: inst.name = "JH "; break;
- case 12: inst.name = "JOP "; break;
- case 13: inst.name = "SBO "; break;
- case 14: inst.name = "SBZ "; break;
- case 15: inst.name = "TB "; break;
- }
-
- } else if (op < 0x4000 && !(op >= 0x3000 && op < 0x3800)) {
- inst.op1.type = (op & 0x30) >> 4;
- inst.op1.val = (op & 15);
- inst.op1.dest = false;
- inst.op2.type = OP_REG;
- inst.op2.val = (op & 0x3c0) >> 6;
- inst.op2.dest = true;
-
- switch ((op & 0x1c00) >> 10) {
- case 0: inst.name = "COC ";
- inst.op2.dest = false;
- break;
- case 1: inst.name = "CZC ";
- inst.op2.dest = false;
- break;
- case 2: inst.name = "XOR "; break;
- case 3: inst.name = "XOP "; break;
- case 6: inst.name = "MPY ";
- // inst.op2.type = OP_MPY;
- break;
- case 7: inst.name = "DIV ";
- // inst.op2.type = OP_DIV;
- break;
- }
-
- } else if (op >= 0x3000 && op < 0x3800) {
- inst.op1.type = (op & 0x30) >> 4;
- inst.op1.val = (op & 15);
- inst.op2.type = OP_CNT;
- inst.op2.val = (op & 0x3c0) >> 6;
- if (inst.op2.val == 0) inst.op2.val = 16;
- inst.op1.byteop = (inst.op2.val <= 8);
-
- inst.name = (op < 0x3400 ? "LDCR" : "STCR");
- inst.op1.dest = (op >= 0x3400);
-
- } else {
- inst.op1.type = (op & 0x30) >> 4;
- inst.op1.val = (op & 15);
- inst.op2.type = (op & 0x0c00) >> 10;
- inst.op2.val = (op & 0x3c0) >> 6;
- inst.op2.dest = true;
- inst.op1.byteop = inst.op2.byteop = ((op & 0x1000) != 0);
-
- switch ((op & 0xf000) >> 12) {
- case 4: inst.name = "SZC "; break;
- case 5: inst.name = "SZCB"; break;
- case 6: inst.name = "S "; break;
- case 7: inst.name = "SB "; break;
- case 8: inst.name = "C ";
- inst.op2.dest = false;
- break;
- case 9: inst.name = "CB ";
- inst.op2.dest = false;
- break;
- case 10: inst.name = "A "; break;
- case 11: inst.name = "AB "; break;
- case 12: inst.name = "MOV ";
- inst.op2.dest = OP_DEST_KILLED;
- break;
- case 13: inst.name = "MOVB";
- inst.op2.dest = OP_DEST_KILLED;
- break;
- case 14: inst.name = "SOC "; break;
- case 15: inst.name = "SOCB"; break;
- }
- }
-
- // Figure out the ea for the operands
- operand_complete(&inst.op1, &pc);
- operand_complete(&inst.op2, &pc);
-
- // And the instruction for X
- if (inst.op2.type == OP_INST) {
- inst.op2.val = MEMORY_READ_WORD(inst.op1.ea);
- }
-
- *ins = inst;
- }
-
- /*
- * Tell if the operand has any effect on a register;
- * return a bitmap for each
- */
- static void
- derive_register_access(Instruction *inst, Operand *op,
- int *read, int *written)
- {
- switch (op->type)
- {
- case OP_REG:
- if (op->dest != OP_DEST_KILLED)
- *read |= (1 << op->val);
- if (op->dest)
- *written |= (1 << op->val);
-
- // multiply writes two registers
- if ((inst->opcode >= 0x3800 && inst->opcode < 0x3C00)
- && op->dest)
- *written |= (1 << (op->val+1));
-
- // divide reads and writes two registers
- if (inst->opcode >= 0x3C00 && inst->opcode < 0x4000) {
- if (op->dest)
- *read |= (1 << (op->val+1));
- else
- *written |= (1 << (op->val+1));
- }
- break;
-
- case OP_IND:
- case OP_ADDR:
- case OP_INC:
- if (op->type != OP_ADDR || op->val != 0)
- *read |= (1 << op->val);
-
- if (op->type == OP_INC)
- *written |= (1 << op->val);
-
- // memory write to register?
- if (op->ea >= inst->wp && op->ea < inst->wp + 32) {
- if (op->dest != OP_DEST_KILLED)
- *read |= (1 << ((op->ea - inst->wp) >> 1));
- if (op->dest)
- *written |= (1 << ((op->ea - inst->wp) >> 1));
- }
- break;
-
- case OP_INST:
- {
- Instruction xinst;
- instruction_decode(op->val,
- inst->pc, inst->wp, inst->status,
- &xinst);
-
- // watch out for recursion
- if (xinst.op1.type != OP_INST
- && xinst.op2.type != OP_INST)
- {
- derive_register_access(&xinst, &xinst.op1, read, written);
- derive_register_access(&xinst, &xinst.op2, read, written);
- }
- else
- {
- // panic
- *read = *written = -1;
- }
- }
- break;
- }
- }
-
- /*
- * Update register view according to effects of
- * previously executed instruction, including change to WP,
- * reads and writes to register.
- */
- static void
- register_update_view(Instruction *inst, u16 wp)
- {
- int reg;
- int read, written;
- u16 regs[16];
-
- // is new register set changing?
-
- if (wp != register_view) {
- register_view = wp;
-
- for (reg = 0; reg < 16; reg++) {
- regs[reg] = MEMORY_READ_WORD((reg<<1) + register_view);
- }
- report_status(STATUS_CPU_REGISTER_VIEW, register_view, regs);
-
- // don't bother reporting effects of previous instruction
- return;
- }
-
- // report accessed registers from prevous instruction
-
- read = written = 0;
- derive_register_access(inst, &inst->op1, &read, &written);
- derive_register_access(inst, &inst->op2, &read, &written);
-
- for (reg = 0; reg < 16; reg++) {
- if (written & (1 << reg)) {
- report_status(
- STATUS_CPU_REGISTER_WRITE,
- reg, MEMORY_READ_WORD((reg<<1) + inst->wp));
- } else if (read & (1 << reg)) {
- report_status(
- STATUS_CPU_REGISTER_READ,
- reg, MEMORY_READ_WORD((reg<<1) + inst->wp));
- }
- }
- }
-
- void
- debugger_register_clear_view(void)
- {
- u16 regs[16];
- int reg;
-
- for (reg = 0; reg < 16; reg++) {
- regs[reg] = MEMORY_READ_WORD((reg<<1) + register_view);
- }
- report_status(STATUS_CPU_REGISTER_VIEW, register_view, regs);
- }
-
- static char *hexstr = "0123456789ABCDEF";
-
- static char *
- hex2(char *buf, u8 val)
- {
- *buf++ = hexstr[(val & 0xf0) >> 4];
- *buf++ = hexstr[val & 0xf];
- return buf;
- }
-
- static char *
- hex4(char *buf, u16 val)
- {
- *buf++ = hexstr[(val & 0xf000) >> 12];
- *buf++ = hexstr[(val & 0xf00) >> 8];
- *buf++ = hexstr[(val & 0xf0) >> 4];
- *buf++ = hexstr[val & 0xf];
- return buf;
- }
-
- /*
- * Setup a memory view, returning a bool telling
- * whether the view changed.
- */
- static bool
- memory_view_setup(Memory *s, MemoryView view, u16 addr, int len)
- {
- bool changed = false;
- mem_domain dmn;
- u8 *mem;
- mrstruct *area;
-
- s->which = view;
- s->addr = addr;
- s->len = len;
-
- if (debugger_memory_view_size[s->which] <= 0) {
- debugger_memory_view_size[s->which] = 16;
- }
-
- // get base address fixed at multiple of view size
- if ((s->addr < s->base ||
- s->addr + len >= s->base + debugger_memory_view_size[s->which])
- || s->base % debugger_memory_view_size[s->which])
- {
- s->base = s->addr - (s->addr % debugger_memory_view_size[s->which]);
- changed = true;
- }
-
- // if not enough room for operand, fudge base address
- if (s->base + debugger_memory_view_size[s->which] < s->addr + len)
- {
- s->base = s->addr;
- changed = true;
- }
-
- // set up memory pointer
- dmn = (view == MEMORY_VIEW_VIDEO) ? md_video :
- (view == MEMORY_VIEW_GRAPHICS) ? md_graphics :
- (view == MEMORY_VIEW_SPEECH) ? md_speech : md_cpu;
-
- //mem = FLAT_MEMORY_PTR(dmn, s->base);
- area = THE_AREA(dmn, s->base);
- if (area->areamemory)
- mem = area->areamemory + (s->base & (AREASIZE-1));
- else
- mem = zeroes;
-
-
- if (mem != s->mem)
- changed = true;
-
- s->mem = mem;
-
- return changed;
- }
-
- /*
- * For a given address reference, select a view for the
- * type of memory it is referencing.
- */
-
- INLINE int memory_distance(u16 a, u16 b)
- {
- return (a < 0x8000 && b < 0x8000) ? a - b :
- (a < 0x8000 && b >= 0x8000) ? a - (0x10000 - b) :
- (a >= 0x8000 && b < 0x8000) ? (0x10000 - a) - b :
- (0x10000 - a) - (0x10000 - b);
- }
-
- static Memory *
- memory_view_get(u16 addr, int len, bool dest, Memory *using, bool *changed)
- {
- MemoryView view = dest ? MEMORY_VIEW_CPU_2 : MEMORY_VIEW_CPU_1;
- Memory *s;
-
- // !!! warning, this assumes a 99/4A with this memory
- // configuration
- if (addr >= 0x8400 && addr < 0xa000) {
- addr &= 0x9c02;
-
- switch (addr) {
- case 0x8800:
- case 0x8c00:
- case 0x8c02:
- if (!vdp_mmio_addr_is_complete())
- return 0L;
- addr = vdp_mmio_get_addr();
- view = MEMORY_VIEW_VIDEO;
- break;
- case 0x9000:
- case 0x9400:
- if (!speech_mmio_addr_is_complete())
- return 0L;
- addr = speech_mmio_get_addr();
- view = MEMORY_VIEW_SPEECH;
- break;
- case 0x9800:
- case 0x9802:
- case 0x9c00:
- case 0x9c02:
- if (!grom_mmio_addr_is_complete())
- return 0L;
- addr = grom_mmio_get_addr();
- view = MEMORY_VIEW_GRAPHICS;
- break;
- }
- }
-
- // divide cpu views 1 and 2 into source and
- // destination, or, based on distance from previous view
-
- if (view == MEMORY_VIEW_CPU_1 || view == MEMORY_VIEW_CPU_2) {
- int dist1 = memory_distance(views[MEMORY_VIEW_CPU_1].addr, addr);
- int dist2 = memory_distance(views[MEMORY_VIEW_CPU_2].addr, addr);
-
- if (dist1 > dist2 + 32 ||
- (views[MEMORY_VIEW_CPU_1].coverage >
- views[MEMORY_VIEW_CPU_2].coverage + 32))
- view = MEMORY_VIEW_CPU_2;
- else if (dist2 > dist1 + 32 ||
- (views[MEMORY_VIEW_CPU_2].coverage >
- views[MEMORY_VIEW_CPU_1].coverage + 32))
- view = MEMORY_VIEW_CPU_1;
-
- views[view].coverage++;
- }
-
-
- // setup the view info
- s = &views[view];
-
- *changed = memory_view_setup(s, view, addr, len);
-
- return s;
- }
-
- /*
- * Update views of memory according to effect of
- * previous instruction
- */
- static void
- memory_update_views(Instruction *inst)
- {
- Memory *view1 = 0L, *view2 = 0L;
- bool view1changed, view2changed;
-
- // pick a view for each memory operand
-
- if (OP_IS_MEMORY(inst->op1)) {
- view1 = memory_view_get(inst->op1.ea,
- inst->op1.byteop ? 1 : 2,
- inst->op1.dest,
- NULL,
- &view1changed);
- }
-
- if (OP_IS_MEMORY(inst->op2)) {
- view2 = memory_view_get(inst->op2.ea,
- inst->op2.byteop ? 1 : 2,
- inst->op2.dest,
- view1,
- &view2changed);
- }
-
- // don't show the same one twice
-
- if (view1 && view1 == view2)
- view2 = 0L;
-
- // update each view
-
- if (view1) {
- if (view1changed)
- report_status(STATUS_MEMORY_VIEW, view1);
-
- report_status(inst->op1.dest ?
- STATUS_MEMORY_WRITE :
- STATUS_MEMORY_READ, view1);
- }
-
- if (view2) {
- if (view2changed)
- report_status(STATUS_MEMORY_VIEW, view2);
-
- report_status(inst->op2.dest ?
- STATUS_MEMORY_WRITE :
- STATUS_MEMORY_READ, view2);
- }
- }
-
- static u16
- memory_view_real_address(Memory *s)
- {
- switch (s->which)
- {
- case MEMORY_VIEW_VIDEO:
- s->len = 1;
- if (vdp_mmio_addr_is_complete())
- s->addr = vdp_mmio_get_addr();
- return true;
- case MEMORY_VIEW_GRAPHICS:
- s->len = 1;
- if (grom_mmio_addr_is_complete())
- s->addr = grom_mmio_get_addr();
- return true;
- case MEMORY_VIEW_SPEECH:
- s->len = 1;
- if (speech_mmio_addr_is_complete())
- s->addr = speech_mmio_get_addr();
- return true;
- }
- return false;
- }
-
- void
- debugger_memory_clear_views(void)
- {
- MemoryView view;
- Memory *s;
- bool memory_mapped;
-
- for (view = MEMORY_VIEW_CPU_1;
- view < MEMORY_VIEW_COUNT;
- view++)
- {
- s = &views[view];
- memory_mapped = memory_view_real_address(s);
- memory_view_setup(s, view, s->addr, 0);
- report_status(STATUS_MEMORY_VIEW, s);
- }
- }
-
- /*
- * Update view of instruction. When a previous instruction
- * is being viewed, we send any destination operands changed.
- * For a current instruction, we preview the values of the operands
- * and send those, as well as a disassembly.
- */
- static void
- instruction_update_view(Instruction *inst, bool after)
- {
- char hex[16];
- char disasm[64];
- char op1[32], *op1ptr;
- char op2[32], *op2ptr;
-
- if (!after) {
- // tell about the system registers
- report_status(STATUS_CPU_PC, inst->pc);
- report_status(STATUS_CPU_WP, inst->wp);
- report_status(STATUS_CPU_STATUS, inst->status);
-
- // get hex representation of instruction
- sprintf(hex, "%04X=%04X", inst->pc, inst->opcode);
-
- // get disassembly with operand representations
- op1ptr = debugger_instruction_operand_print(&inst->op1, op1);
- op2ptr = debugger_instruction_operand_print(&inst->op2, op2);
- if (!op1ptr) {
- op1ptr = op2ptr;
- op2ptr = 0L;
- }
- sprintf(disasm, "%s%s%s",
- op1ptr ? op1ptr : "",
- op2ptr ? "," : "",
- op2ptr ? op2ptr : "");
-
- // get operand values
- op1ptr = debugger_operand_value_print(
- inst,
- &inst->op1,
- debugger_operand_view_verbose,
- false /*after*/,
- op1);
-
- op2ptr = debugger_operand_value_print(
- inst,
- &inst->op2,
- debugger_operand_view_verbose,
- false /*after*/,
- op2);
-
- if (!op1ptr) {
- op1ptr = op2ptr;
- op2ptr = 0L;
- }
-
- // send it to frontend
- report_status(STATUS_CPU_INSTRUCTION,
- inst,
- hex,
- disasm,
- op1ptr,
- op2ptr);
- } else {
- // afterwards, show destination operands
-
- // get operand values
- op1ptr = debugger_operand_value_print(
- inst,
- &inst->op1,
- debugger_operand_view_verbose,
- true /*after*/,
- op1);
-
- op2ptr = debugger_operand_value_print(
- inst,
- &inst->op2,
- debugger_operand_view_verbose,
- true /*after*/,
- op2);
-
- if (!op1ptr) {
- op1ptr = op2ptr;
- op2ptr = 0L;
- }
-
- // send it to frontend
- report_status(STATUS_CPU_INSTRUCTION_LAST,
- inst,
- op1ptr,
- op2ptr);
- }
- }
-
- void
- debugger_instruction_clear_view(void)
- {
- // send refresh signal to frontend
- report_status(STATUS_CPU_INSTRUCTION_LAST,
- 0L,
- 0L,
- 0L);
- }
-
- /*
- * Utility for status reporters. Given the slot, it writes a one-line hex dump to
- * the given buffer, and sets start/astart and end/aend to point to the extent
- * of the last memory access within the buffer. (These will be spaces.)
- *
- * addr_separator: char appearing between address and bytes
- * byte_separator: char appearing between each hex byte
- * ascii_separator: char appearing between hex field and ascii field
- * line_separator: char appearing between lines, and at end
- */
- void
- debugger_hex_dump_line(Memory * slot, int offset, int length,
- char addr_separator, char byte_separator,
- char ascii_separator, char line_separator,
- char *buffer, int bufsz,
- char **start, char **end,
- char **astart, char **aend)
- {
- char *dumpptr = buffer, *asciiptr = dumpptr + 6+length*3;
- u16 idx, addr;
- u8 *bytes;
-
- if (asciiptr + length + 1 >= buffer + bufsz)
- {
- length = debugger_hex_dump_chars_to_bytes(bufsz-1);
- asciiptr = dumpptr + 6+length*3;
- my_assert(asciiptr + length < buffer + bufsz);
- }
-
- *dumpptr = 0;
- *start = *end = *astart = *aend = 0L;
-
- bytes = slot->mem + offset;
- addr = slot->base + offset;
-
- *dumpptr++ = MEMORY_VIEW_TOKEN(slot->which);
- dumpptr = hex4(dumpptr, addr);
- *dumpptr++ = addr_separator;
-
- if (slot->len
- && addr > slot->addr
- && addr < slot->addr + slot->len)
- {
- *start = dumpptr;
- *astart = asciiptr;
- }
-
- idx = 0;
- while (idx < length) {
- u8 ch;
-
- if (slot->len && addr + idx == slot->addr) {
- *start = dumpptr;
- *astart = asciiptr;
- }
-
- ch = bytes[idx];
-
- dumpptr = hex2(dumpptr, ch);
- *asciiptr++ = isprint(ch) ? ch : '.';
- idx++;
-
- if (slot->len && addr + idx == slot->addr + slot->len) {
- *end = dumpptr;
- *aend = asciiptr;
- }
- if (idx < length)
- *dumpptr++ = byte_separator;
- }
-
- if (slot->len
- && addr + length > slot->addr
- && addr + length < slot->addr + slot->len)
- {
- *end = dumpptr;
- *aend = asciiptr;
- }
-
- *dumpptr++ = ascii_separator;
- *asciiptr++ = line_separator;
- *asciiptr = 0;
- }
-
- /*
- * How long will this text be?
- */
- int
- debugger_hex_dump_bytes_to_chars(int bytes)
- {
- return bytes * 4 + 6 + 1 + 1;
- }
-
- /*
- * How many bytes fit in this length?
- */
- int
- debugger_hex_dump_chars_to_bytes(int chars)
- {
- return (chars - 6 - 1 - 1) / 4;
- }
-
- /*
- * Breakpoint manager
- */
-
- typedef struct bkpt {
- u16 pc; // location of bkpt
- struct bkpt *next;
- } bkpt;
-
- static bkpt *breakpoints;
-
- // check to see if the address matches a breakpoint
- int
- debugger_check_breakpoint(u16 pc)
- {
- bkpt *ptr = breakpoints;
- while (ptr)
- if (ptr->pc == pc)
- return 1;
- else
- ptr = ptr->next;
- return 0;
- }
-
- // add the address to the list of breakpoints
- static void
- debugger_set_breakpoint(u16 pc)
- {
- bkpt *ptr;
-
- if (debugger_check_breakpoint(pc))
- return;
-
- ptr = (bkpt *)xmalloc(sizeof(bkpt));
- ptr->pc = pc;
- ptr->next = breakpoints;
- breakpoints = ptr;
- }
-
- // remove the address from the list of breakpoints
- static void
- debugger_reset_breakpoint(u16 pc)
- {
- bkpt *ptr, *prev;
-
- prev = 0L;
- ptr = breakpoints;
- while (ptr && ptr->pc != pc)
- {
- prev = ptr;
- ptr = ptr->next;
- }
-
- if (!ptr) return;
-
- if (prev)
- prev->next = ptr->next;
- else
- breakpoints = ptr->next;
- }
-
- DECL_SYMBOL_ACTION(debugger_breakpoint)
- {
- if (task == csa_READ) {
- static bkpt *list;
-
- if (!iter) list = breakpoints;
-
- if (list == 0L)
- return 0;
-
- command_arg_set_num(SYM_ARG_1st, list->pc);
- list = list->next;
- } else {
- int val;
-
- command_arg_get_num(SYM_ARG_1st, &val);
- debugger_set_breakpoint(val);
- }
- return 1;
- }
-
- DECL_SYMBOL_ACTION(debugger_clear_breakpoint)
- {
- int val;
-
- command_arg_get_num(SYM_ARG_1st, &val);
- debugger_reset_breakpoint(val);
- return 1;
- }
-
-
- DECL_SYMBOL_ACTION(debugger_list_breakpoints)
- {
- static bkpt *list;
-
- list = breakpoints;
-
- logger(_L|LOG_USER, "Active breakpoints:\n");
- if (list == 0L)
- {
- logger(_L|LOG_USER, "\t<none>\n");
- return 1;
- }
-
- while (list)
- {
- logger(_L|LOG_USER, "\t>%04X\n", list->pc);
- list = list->next;
- }
- return 1;
- }
-
-
- /*
- * Entry point for debugger backend, entered before every instruction
- * executed when ST_DEBUG is set in the stateflag.
- */
-
- static Instruction last;
- static bool last_valid;
-
- void
- debugger(void)
- {
- Instruction inst;
-
- report_status(STATUS_DEBUG_REFRESH);
-
- // Show effects of previous instruction
- if (last_valid) {
- memory_update_views(&last);
- register_update_view(&last, wp);
- instruction_update_view(&last, true /*after*/);
- } else {
- debugger_memory_clear_views();
- debugger_register_clear_view();
- debugger_instruction_clear_view();
- }
-
- // Get a status word for this instruction
- statusto9900();
-
- // Decode the current instruction
- instruction_decode(MEMORY_READ_WORD(pc),
- pc, wp, status,
- &inst);
-
- if (last.pc != inst.pc) {
- // Show current instruction
- instruction_update_view(&inst, false /*after*/);
- }
-
- // Save instruction
- last = inst;
- last_valid = true;
- }
-
- void
- debugger_init(void)
- {
- command_symbol_table *debugcommands =
- command_symbol_table_new("Debugger Options",
- "These commands control the debugger",
-
- command_symbol_new("BreakPoint",
- "Add a breakpoint at the given PC",
- c_DYNAMIC|c_SESSION_ONLY,
- debugger_breakpoint,
- RET_FIRST_ARG,
- command_arg_new_num
- ("address",
- "PC address at which to break",
- NULL /* action */ ,
- NEW_ARG_NUM(u16),
- NULL /* next */ )
- ,
-
- command_symbol_new("ClearBreakPoint",
- "Remove breakpoint at the given PC",
- c_STATIC|c_DONT_SAVE,
- debugger_clear_breakpoint,
- RET_FIRST_ARG,
- command_arg_new_num
- ("address",
- "PC address of breakpoint",
- NULL /* action */ ,
- NEW_ARG_NUM(u16),
- NULL /* next */ )
- ,
-
- command_symbol_new("ListBreakPoints",
- "List active breakpoints",
- c_STATIC|c_DONT_SAVE,
- debugger_list_breakpoints,
- NULL /*ret*/,
- NULL /*args*/,
-
- NULL /*next*/))),
- NULL /* sub */,
- NULL /* next */
- );
-
- command_symbol_table_add_subtable(universe, debugcommands);
-
- memset((void *)&views, 0, sizeof(views));
- register_view = 0;
- }
-
- /*
- * Force next update to refresh all status items
- */
- void
- debugger_refresh(void)
- {
- last_valid = false;
- }
-
- void
- debugger_enable(bool enable)
- {
- if (enable) {
- system_debugger_enabled(true);
- if (!(stateflag & ST_DEBUG)) {
- stateflag |= ST_DEBUG | ST_SINGLESTEP;
- debugger_refresh();
- debugger();
- }
- } else if (!enable && (stateflag & ST_DEBUG)) {
- stateflag &= ~ST_DEBUG;
- system_debugger_enabled(false);
- debugger_refresh();
- }
- }
-
-