// license:BSD-3-Clause // copyright-holders:Christian Brunschen /*************************************************************************** * * es5510.c - Ensoniq ES5510 (ESP) emulation * by Christian Brunschen * ***************************************************************************/ #include #include "emu.h" #include "debugger.h" #include "es5510.h" #include "cpu/m68000/m68000.h" static const int32_t MIN_24 = -(1 << 23); static const int32_t MAX_24 = (1 << 23) - 1; static const int64_t MIN_48 = -(s64(1) << 47); static const int64_t MAX_48 = (s64(1) << 47) - 1; #define SIGN_BIT_24 (0x00800000) #define GET_SIGN_BIT_24(x) ((x) & SIGN_BIT_24) #define IS_NEGATIVE(x) (((x) & SIGN_BIT_24) != 0) #define CARRY_OUT_24 (0x01000000) static inline int32_t SX(int32_t x) { return IS_NEGATIVE(x) ? x | 0xff000000 : x & 0x00ffffff; } static inline int32_t SC(int32_t x) { return x & 0x00ffffff; } static inline int64_t SX64(int64_t x) { return (x & s64(0x0000800000000000U)) ? x | s64(0xffff000000000000U) : x & s64(0x0000ffffffffffffU); } //static inline int64_t SC64(int64_t x) { return x & s64(0x0000ffffffffffffU); } #define VERBOSE 0 #define VERBOSE_EXEC 0 #if VERBOSE static inline void ATTR_PRINTF(1,2) log_to_stderr(const char *format, ...) { va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); } #define LOG(x) do { log_to_stderr x; } while(0) #else #define LOG(x) #endif #if VERBOSE_EXEC static int exec_cc = 0; #define LOG_EXEC(x) do { if (!exec_cc) LOG(x); } while(0) #else #define LOG_EXEC(x) #endif const device_type ES5510 = device_creator; #define FLAG_N (1 << 7) #define FLAG_C (1 << 6) #define FLAG_V (1 << 5) #define FLAG_LT (1 << 4) #define FLAG_Z (1 << 3) #define FLAG_NOT (1 << 2) #define FLAG_MASK (FLAG_N | FLAG_C | FLAG_V | FLAG_LT | FLAG_Z) char *stpcpy_int (char *dst, const char *src) { const size_t len = strlen (src); return (char *) memcpy (dst, src, len + 1) + len; } inline static uint8_t setFlag(uint8_t ccr, uint8_t flag) { return ccr | flag; } inline static uint8_t clearFlag(uint8_t ccr, uint8_t flag) { return ccr & ~flag; } inline static uint8_t setFlagTo(uint8_t ccr, uint8_t flag, bool set) { return set ? setFlag(ccr, flag) : clearFlag(ccr, flag); } inline static bool isFlagSet(uint8_t ccr, uint8_t flag) { return (ccr & flag) != 0; } inline static int32_t add(int32_t a, int32_t b, uint8_t &flags) { int32_t aSign = a & SIGN_BIT_24; int32_t bSign = b & SIGN_BIT_24; int32_t result = a + b; int32_t resultSign = result & SIGN_BIT_24; bool overflow = (aSign == bSign) && (aSign != resultSign); bool carry = result & CARRY_OUT_24; bool negative = resultSign != 0; bool lessThan = (overflow && !negative) || (!overflow && negative); flags = setFlagTo(flags, FLAG_C, carry); flags = setFlagTo(flags, FLAG_N, negative); flags = setFlagTo(flags, FLAG_Z, result == 0); flags = setFlagTo(flags, FLAG_V, overflow); flags = setFlagTo(flags, FLAG_LT, lessThan); return SC(result); } inline static int32_t saturate(int32_t value, uint8_t &flags, bool negative) { if (isFlagSet(flags, FLAG_V)) { setFlagTo(flags, FLAG_N, negative); return negative ? MIN_24 : MAX_24; } else { return value; } } inline static int32_t negate(int32_t value) { return ((value ^ 0x00ffffff) + 1) & 0x00ffffff; } inline static int32_t asl(int32_t value, int shift, uint8_t &flags) { int32_t signBefore = value & SIGN_BIT_24; int32_t result = value << shift; int32_t signAfter = result & SIGN_BIT_24; bool overflow = signBefore != signAfter; flags = setFlagTo(flags, FLAG_V, overflow); return saturate(result, flags, signBefore != 0); } es5510_device::es5510_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : cpu_device(mconfig, ES5510, "ES5510", tag, owner, clock, "es5510", __FILE__) { // Initialize ESP to mostly zeroed, configured for 64k samples of delay line memory, running (not halted) halt_asserted = false; icount = 0; pc = 0; state = STATE_HALTED; memset(gpr, 0, 0xc0 * sizeof(gpr[0])); ser0r = 0; ser0l = 0; ser1r = 0; ser1l = 0; ser2r = 0; ser2l = 0; ser3r = 0; ser3l = 0; machl = 0; dil = 0; memsiz = 0x00ffffff; memmask = 0x00000000; memincrement = 0x01000000; memshift = 24; dlength = 0; abase = 0; bbase = 0; dbase = 0; sigreg = 1; mulshift = 1; ccr = 0; cmr = 0; dol[0] = dol[1] = 0; dol_count = 0; memset(instr, 0, 160 * sizeof(instr[0])); memset(dram, 0, (1<<20) * sizeof(dram[0])); dol_latch = 0; dil_latch = 0; dadr_latch = 0; gpr_latch = 0; instr_latch = 0; ram_sel = 0; host_control = 0; pc = 0; memset(&alu, 0, sizeof(alu)); memset(&mulacc, 0, sizeof(mulacc)); } typedef es5510_device::alu_op_t alu_op_t; typedef es5510_device::op_select_t op_select_t; typedef es5510_device::op_src_dst_t op_src_dst_t; static inline const char * REGNAME(uint8_t r) { static char rn[8]; if (r < 234) { sprintf(rn, "GPR_%02x", r); return rn; } switch(r) { case 234: return "SER0R"; case 235: return "SER0L"; case 236: return "SER1R"; case 237: return "SER1L"; case 238: return "SER2R"; case 239: return "SER2L"; case 240: return "SER3R"; case 241: return "SER3L"; case 242: return "MACL"; case 243: return "MACH"; case 244: return "DIL/MEMSIZ"; case 245: return "DLENGTH"; case 246: return "ABASE"; case 247: return "BBASE"; case 248: return "DBASE"; case 249: return "SIGREG"; case 250: return "CCR"; case 251: return "CMR"; case 252: return "MINUS1"; case 253: return "MIN"; case 254: return "MAX"; case 255: return "ZERO"; } return nullptr; } static inline char * DESCRIBE_REG(char *s, uint8_t r, const char *name) { if (name && *name) { return s + sprintf(s, "%s/%s", REGNAME(r), name); } else { return stpcpy_int(s, REGNAME(r)); } // never executed //return 0; } const alu_op_t es5510_device::ALU_OPS[16] = { { 2, "ADD" }, { 2, "SUB" }, { 2, "ADDU" }, { 2, "SUBU" }, { 2, "CMP" }, { 2, "AND" }, { 2, "OR" }, { 2, "XOR" }, { 1, "ABS" }, { 1, "MOV" }, { 1, "ASL2" }, { 1, "ASL8" }, { 1, "LS15" }, { 1, "DIFF" }, { 1, "ASR" }, { 0, "END" }, }; // The CMP operation is not affected by being skippable #define OP_CMP (4) const op_select_t es5510_device::OPERAND_SELECT[16] = { { es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG }, { es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_DELAY }, { es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_BOTH }, { es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG }, { es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_BOTH }, { es5510_device::SRC_DST_REG, es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG }, { es5510_device::SRC_DST_REG, es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG }, { es5510_device::SRC_DST_REG, es5510_device::SRC_DST_BOTH, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG }, { es5510_device::SRC_DST_REG, es5510_device::SRC_DST_BOTH, es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG }, { es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG }, { es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_DELAY }, { es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_BOTH }, { es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG }, { es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_BOTH }, { es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_BOTH, es5510_device::SRC_DST_REG, es5510_device::SRC_DST_REG }, { es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_BOTH, es5510_device::SRC_DST_DELAY, es5510_device::SRC_DST_REG }, }; static inline char * DESCRIBE_SRC_DST(char *s, uint8_t reg, const char *regname, op_src_dst_t src_dst) { switch (src_dst) { case es5510_device::SRC_DST_REG: return DESCRIBE_REG(s, reg, regname); case es5510_device::SRC_DST_DELAY: return stpcpy_int(s, "Delay"); case es5510_device::SRC_DST_BOTH: s = DESCRIBE_REG(s, reg, regname); return stpcpy_int(s, ",Delay"); } // should never happen! return s; } const es5510_device::ram_control_t es5510_device::RAM_CONTROL[8] = { { es5510_device::RAM_CYCLE_READ, es5510_device::RAM_CONTROL_DELAY, "Read Delay+%06x" }, { es5510_device::RAM_CYCLE_WRITE, es5510_device::RAM_CONTROL_DELAY, "Write Delay+%06x" }, { es5510_device::RAM_CYCLE_READ, es5510_device::RAM_CONTROL_TABLE_A, "Read Table A+%06x" }, { es5510_device::RAM_CYCLE_WRITE, es5510_device::RAM_CONTROL_TABLE_A, "Write Table A+%06x" }, { es5510_device::RAM_CYCLE_READ, es5510_device::RAM_CONTROL_TABLE_B, "Read Table B+%06x" }, { es5510_device::RAM_CYCLE_DUMP_FIFO, es5510_device::RAM_CONTROL_DELAY, "Read Delay+%06x and Dump FIFO" }, { es5510_device::RAM_CYCLE_READ, es5510_device::RAM_CONTROL_IO, "Read from I/O at %06x" }, { es5510_device::RAM_CYCLE_WRITE, es5510_device::RAM_CONTROL_IO, "Write to I/O at %06x" }, }; static inline char * DESCRIBE_RAM(char *s, uint8_t ramControl, uint32_t gprContents) { return s + sprintf(s, es5510_device::RAM_CONTROL[ramControl].description, SC(gprContents)); } static inline char * DESCRIBE_ALU(char *s, uint8_t opcode, uint8_t aReg, const char *aName, uint8_t bReg, const char *bName, const op_select_t &opSelect) { const alu_op_t &op = es5510_device::ALU_OPS[opcode]; switch (op.operands) { case 0: return stpcpy_int(s, op.opcode); case 1: s += sprintf(s, "%s ", op.opcode); s = DESCRIBE_SRC_DST(s, bReg, bName, opSelect.alu_src); s += sprintf(s, " >"); return DESCRIBE_SRC_DST(s, aReg, aName, opSelect.alu_dst); case 2: s += sprintf(s, "%s ", op.opcode); s = DESCRIBE_SRC_DST(s, aReg, aName, opSelect.alu_src); s += sprintf(s, " "); s = DESCRIBE_REG(s, bReg, bName); s += sprintf(s, " >"); return DESCRIBE_SRC_DST(s, aReg, aName, opSelect.alu_dst); } return s; } static inline char * DESCRIBE_MAC(char *s, uint8_t mac, uint8_t cReg, const char *cName, uint8_t dReg, const char *dName, const op_select_t &opSelect) { if (mac) { s += sprintf(s, "MAC + "); } s = DESCRIBE_SRC_DST(s, cReg, cName, opSelect.mac_src); s += sprintf(s, " * "); s = DESCRIBE_REG(s, dReg, dName); s += sprintf(s, " >"); return DESCRIBE_SRC_DST(s, cReg, cName, opSelect.mac_dst); } static inline char * DESCRIBE_INSTR(char *s, uint64_t instr, uint32_t gpr, const char *aName, const char *bName, const char *cName, const char *dName) { uint8_t dReg = (uint8_t)((instr >> 40) & 0xff); uint8_t cReg = (uint8_t)((instr >> 32) & 0xff); uint8_t bReg = (uint8_t)((instr >> 24) & 0xff); uint8_t aReg = (uint8_t)((instr >> 16) & 0xff); uint8_t aluOpcode = (uint8_t)((instr >> 12) & 0x0f); uint8_t operandSelect = (uint8_t)((instr >> 8) & 0x0f); uint8_t skip = (uint8_t)((instr >> 7) & 0x01); uint8_t mac = (uint8_t)((instr >> 6) & 0x01); uint8_t ramControl = (uint8_t)((instr >> 3) & 0x07); const op_select_t &opSelect = es5510_device::OPERAND_SELECT[operandSelect]; s = DESCRIBE_ALU(s, aluOpcode, aReg, aName, bReg, bName, opSelect); s += sprintf(s, "; "); s = DESCRIBE_MAC(s, mac, cReg, cName, dReg, dName, opSelect); s += sprintf(s, "; "); s = DESCRIBE_RAM(s, ramControl, gpr); if (skip) { s += sprintf(s, "; skippable"); } return s; } READ8_MEMBER(es5510_device::host_r) { // printf("%06x: DSP read offset %04x (data is %04x)\n",space.device().safe_pc(),offset,dsp_ram[offset]); // VFX hack if (core_stricmp(space.machine().system().name, "vfx") == 0) { if (space.device().safe_pc() == 0xc091f0) { return space.device().state().state_int(M68K_D2); } } switch(offset) { case 0x00: LOG(("ES5510: Host Read GPR latch[2]: %02x\n", (gpr_latch >> 16) & 0xff)); return (gpr_latch >> 16) & 0xff; case 0x01: LOG(("ES5510: Host Read GPR latch[1]: %02x\n", (gpr_latch >> 8) & 0xff)); return (gpr_latch >> 8) & 0xff; case 0x02: LOG(("ES5510: Host Read GPR latch[0]: %02x\n", (gpr_latch >> 0) & 0xff)); return (gpr_latch >> 0) & 0xff; case 0x03: LOG(("ES5510: Host Read INSTR latch[5]: %02x\n", (uint8_t)((instr_latch >> 40) & 0xff))); return (instr_latch >> 40) & 0xff; case 0x04: LOG(("ES5510: Host Read INSTR latch[4]: %02x\n", (uint8_t)((instr_latch >> 32) & 0xff))); return (instr_latch >> 32) & 0xff; case 0x05: LOG(("ES5510: Host Read INSTR latch[3]: %02x\n", (uint8_t)((instr_latch >> 24) & 0xff))); return (instr_latch >> 24) & 0xff; case 0x06: LOG(("ES5510: Host Read INSTR latch[2]: %02x\n", (uint8_t)((instr_latch >> 16) & 0xff))); return (instr_latch >> 16) & 0xff; case 0x07: LOG(("ES5510: Host Read INSTR latch[1]: %02x\n", (uint8_t)((instr_latch >> 8) & 0xff))); return (instr_latch >> 8) & 0xff; case 0x08: LOG(("ES5510: Host Read INSTR latch[0]: %02x\n", (uint8_t)((instr_latch >> 0) & 0xff))); return (instr_latch >> 0) & 0xff; case 0x09: LOG(("ES5510: Host Read DIL latch[2]: %02x\n", (dil_latch >> 16) & 0xff)); return (dil_latch >> 16) & 0xff; case 0x0a: LOG(("ES5510: Host Read DIL latch[1]: %02x\n", (dil_latch >> 8) & 0xff)); return (dil_latch >> 8) & 0xff; case 0x0b: LOG(("ES5510: Host Read DIL latch[0]: %02x\n", 0)); return 0; case 0x0c: LOG(("ES5510: Host Read DOL latch[2]: %02x\n", (dol_latch >> 16) & 0xff)); return (dol_latch >> 16) & 0xff; case 0x0d: LOG(("ES5510: Host Read DOL latch[1]: %02x\n", (dol_latch >> 8) & 0xff)); return (dol_latch >> 8) & 0xff; case 0x0e: LOG(("ES5510: Host Read DOL latch[0]: %02x\n", 0xff)); return 0xff; case 0x0f: LOG(("ES5510: Host Read DADR latch[2]: %02x\n", (dadr_latch >> 16) & 0xff)); return (dadr_latch >> 16) & 0xff; case 0x10: LOG(("ES5510: Host Read DADR latch[1]: %02x\n", (dadr_latch >> 8) & 0xff)); return (dadr_latch >> 8) & 0xff; case 0x11: LOG(("ES5510: Host Read DADR latch[0]: %02x\n", (dadr_latch >> 0) & 0xff)); return (dadr_latch >> 0) & 0xff; case 0x12: LOG(("ES5510: Host Reading Host Control\n")); return 0; // Host Control case 0x16: return 0x27; // Program Counter, for test purposes only } // default: 0. return 0x00; } WRITE8_MEMBER(es5510_device::host_w) { #if VERBOSE static char buf[1024]; #endif switch (offset) { case 0x00: gpr_latch = (gpr_latch&0x00ffff) | ((data&0xff)<<16); LOG(("ES5510: Host Write GPR latch[2] = %02x -> %06x (%d)\n", data, gpr_latch, SX(gpr_latch))); break; case 0x01: gpr_latch = (gpr_latch&0xff00ff) | ((data&0xff)<< 8); LOG(("ES5510: Host Write GPR latch[1] = %02x -> %06x (%d)\n", data, gpr_latch, SX(gpr_latch))); break; case 0x02: gpr_latch = (gpr_latch&0xffff00) | ((data&0xff)<< 0); LOG(("ES5510: Host Write GPR latch[0] = %02x -> %06x (%d)\n", data, gpr_latch, SX(gpr_latch))); break; /* 0x03 to 0x08 INSTR Register */ case 0x03: instr_latch = ((instr_latch&0x00ffffffffffU) | ((int64_t)data&0xff)<<40); LOG(("%s",string_format("ES5510: Host Write INSTR latch[5] = %02x -> %012I64x\n", data, instr_latch).c_str())); break; case 0x04: instr_latch = ((instr_latch&0xff00ffffffffU) | ((int64_t)data&0xff)<<32); LOG(("%s",string_format("ES5510: Host Write INSTR latch[4] = %02x -> %012I64x\n", data, instr_latch).c_str())); break; case 0x05: instr_latch = ((instr_latch&0xffff00ffffffU) | ((int64_t)data&0xff)<<24); LOG(("%s",string_format("ES5510: Host Write INSTR latch[3] = %02x -> %012I64x\n", data, instr_latch).c_str())); break; case 0x06: instr_latch = ((instr_latch&0xffffff00ffffU) | ((int64_t)data&0xff)<<16); LOG(("%s",string_format("ES5510: Host Write INSTR latch[2] = %02x -> %012I64x\n", data, instr_latch).c_str())); break; case 0x07: instr_latch = ((instr_latch&0xffffffff00ffU) | ((int64_t)data&0xff)<< 8); LOG(("%s",string_format("ES5510: Host Write INSTR latch[1] = %02x -> %012I64x\n", data, instr_latch).c_str())); break; case 0x08: instr_latch = ((instr_latch&0xffffffffff00U) | ((int64_t)data&0xff)<< 0); LOG(("%s",string_format("ES5510: Host Write INSTR latch[0] = %02x -> %012I64x\n", data, instr_latch).c_str())); break; /* 0x09 to 0x0b DIL Register (r/o) */ case 0x0c: dol_latch = (dol_latch&0x00ffff) | ((data&0xff)<<16); LOG(("ES5510: Host Write DOL latch[2] = %02x -> %06x (%d)\n", data, dol_latch, SX(dol_latch))); break; case 0x0d: dol_latch = (dol_latch&0xff00ff) | ((data&0xff)<< 8); LOG(("ES5510: Host Write DOL latch[1] = %02x -> %06x (%d)\n", data, dol_latch, SX(dol_latch))); break; case 0x0e: dol_latch = (dol_latch&0xffff00) | ((data&0xff)<< 0); LOG(("ES5510: Host Write DOL latch[0] = %02x -> %06x (%d)\n", data, dol_latch, SX(dol_latch))); break; //TODO: docs says that this always returns 0xff case 0x0f: dadr_latch = (dadr_latch&0x00ffff) | ((data&0xff)<<16); if (ram_sel) { dil_latch = dram[dadr_latch] << 8; } else { dram[dadr_latch] = dol_latch >> 8; } break; case 0x10: dadr_latch = (dadr_latch&0xff00ff) | ((data&0xff)<< 8); break; case 0x11: dadr_latch = (dadr_latch&0xffff00) | ((data&0xff)<< 0); break; /* 0x12 Host Control */ case 0x14: ram_sel = data & 0x80; /* bit 6 is i/o select, everything else is undefined */break; /* 0x16 Program Counter (test purpose, r/o?) */ /* 0x17 Internal Refresh counter (test purpose) */ /* 0x18 Host Serial Control */ case 0x18: LOG(("ES5510: Host Write Host Serial control %02x: %s, %s, ser3 %s, ser2 %s, ser1 %s, ser0 %s\n", data, data&0x80 ? "Master" : "Slave", data&0x40 ? "Sony" : "I2S", data & 0x20 ? "Out" : "In", data & 0x10 ? "Out" : "In", data & 0x08 ? "Out" : "In", data & 0x04 ? "Out" : "In")); break; /* 0x1f Halt enable (w) / Frame Counter (r) */ case 0x1F: LOG(("ES5510: Host Write Halt Enable %02x; HALT line is %d\n", data, halt_asserted)); if (halt_asserted) { LOG(("ES5510: Host Write to Halt Enable while HALT line is asserted: Halting!\n")); state = STATE_HALTED; } break; case 0x80: /* Read select - GPR + INSTR */ LOG(("%s",string_format("ES5510: Host Read INSTR+GPR %02x (%s): %012I64x %06x (%d)\n", data, REGNAME(data & 0xff), instr[data] & 0xffffffffffffU, gpr[data] & 0xffffff, gpr[data]).c_str())); /* Check if an INSTR address is selected */ if (data < 0xa0) { instr_latch = instr[data]; } if (data < 0xc0) { gpr_latch = gpr[data] & 0xffffff; } else if (data >= 0xea) { gpr_latch = read_reg(data); } break; case 0xa0: /* Write select - GPR */ LOG(("ES5510: Host Write GPR %02x (%s): %06x (%d)\n", data, REGNAME(data&0xff), gpr_latch, SX(gpr_latch))); write_reg(data, gpr_latch); break; case 0xc0: /* Write select - INSTR */ #if VERBOSE DESCRIBE_INSTR(buf, instr_latch, gpr[data], nullptr, nullptr, nullptr, nullptr); LOG(("%s",string_format("ES5510: Host Write INSTR %02x %012I64x: %s\n", data, instr_latch&0xffffffffffffU, buf).c_str())); #endif if (data < 0xa0) { instr[data] = instr_latch&0xffffffffffffU; } break; case 0xe0: /* Write select - GPR + INSTR */ #if VERBOSE DESCRIBE_INSTR(buf, instr_latch, gpr_latch, nullptr, nullptr, nullptr, nullptr); LOG(("%s",string_format("ES5510: Host Write INSTR+GPR %02x (%s): %012I64x %06x (%d): %s\n", data, REGNAME(data&0xff), instr_latch, gpr_latch, SX(gpr_latch), buf).c_str())); #endif if (data < 0xa0) { instr[data] = instr_latch; } write_reg(data, gpr_latch); break; } } int16_t es5510_device::ser_r(int offset) { switch(offset) { case 0: return ser0l; case 1: return ser0r; case 2: return ser1l; case 3: return ser1r; case 4: return ser2l; case 5: return ser2r; case 6: return ser3l; case 7: return ser3r; } return 0; } void es5510_device::ser_w(int offset, int16_t data) { switch(offset) { case 0: ser0l = data; break; case 1: ser0r = data; break; case 2: ser1l = data; break; case 3: ser1r = data; break; case 4: ser2l = data; break; case 5: ser2r = data; break; case 6: ser3l = data; break; case 7: ser3r = data; break; } } void es5510_device::device_start() { m_icountptr = &icount; state_add(STATE_GENPC,"GENPC", pc).noshow(); state_add(STATE_GENPCBASE, "CURPC", pc).noshow(); } void es5510_device::device_reset() { pc = 0x00; memset(gpr, 0, sizeof(*gpr) * 0xc0); memset(instr, 0, sizeof(*instr) * 0xa0); memset(dram, 0, sizeof(*dram) * (1<<20)); state = STATE_RUNNING; dil_latch = dol_latch = dadr_latch = gpr_latch = 0; instr_latch = uint64_t(0); ram_sel = 0; host_control = 0; memset(&ram, 0, sizeof(ram_t)); memset(&ram_p, 0, sizeof(ram_t)); memset(&ram_pp, 0, sizeof(ram_t)); } const address_space_config *es5510_device::memory_space_config(address_spacenum spacenum) const { return nullptr; } uint64_t es5510_device::execute_clocks_to_cycles(uint64_t clocks) const { return clocks / 3; } uint64_t es5510_device::execute_cycles_to_clocks(uint64_t cycles) const { return cycles * 3; } uint32_t es5510_device::execute_min_cycles() const { return 1; } uint32_t es5510_device::execute_max_cycles() const { return 1; } uint32_t es5510_device::execute_input_lines() const { return 1; } void es5510_device::execute_set_input(int linenum, int state) { if (linenum == ES5510_HALT) { halt_asserted = (state == ASSERT_LINE); } } void es5510_device::list_program(void(p)(const char *, ...)) { LOG(("ES5501: Starting!\n")); char buf[1024]; bool is_written[0x100], is_read[0x100]; char name[0x100][16]; int addr; for (int i = 0; i < 0x100; i++) { is_written[i] = is_read[i] = false; name[i][0] = '\0'; } for (addr = 0; addr < 0xa0; addr++) { DESCRIBE_INSTR(buf, instr[addr], gpr[addr], nullptr, nullptr, nullptr, nullptr); uint64_t inst = instr[addr]; uint8_t aReg = (uint8_t)((inst >> 16) & 0xff); uint8_t bReg = (uint8_t)((inst >> 24) & 0xff); uint8_t cReg = (uint8_t)((inst >> 32) & 0xff); uint8_t dReg = (uint8_t)((inst >> 40) & 0xff); uint8_t alu_op = (inst >> 12) & 0x0f; if (alu_op == 0x0f) { // END! break; } uint8_t operandSelect = (uint8_t)((inst >> 8) & 0x0f); const op_select_t &opSelect = OPERAND_SELECT[operandSelect]; if (opSelect.mac_src == SRC_DST_REG) { is_read[cReg] = true; } is_read[dReg] = true; if (opSelect.mac_dst != SRC_DST_DELAY) { // either REG or BOTH is_written[cReg] = true; } alu_op_t aluOp = ALU_OPS[alu_op]; if (aluOp.operands == 1) { if (opSelect.alu_src == SRC_DST_REG) { is_read[bReg] = true; } } else if (aluOp.operands == 2) { if (opSelect.alu_src == SRC_DST_REG) { is_read[aReg] = true; } is_read[bReg] = true; } if (opSelect.mac_dst != SRC_DST_DELAY) { // either REG or BOTH is_written[aReg] = true; } } int varIndex = 1; int constIndex = 1; for (int i = 0; i < 0xc0; i++) { if (is_written[i]) { // this is a variable sprintf(name[i], "v_%03d", varIndex++); } else if (is_read[i]) { // this is only read, so a constant - or possibly something updated by the CPU sprintf(name[i], "c_%03d", constIndex++); } else { name[i][0] = 0; } } for (int i = 0xc0; i < 0x100; i++) { name[i][0] = 0; } for (addr = 0; addr < 0xa0; addr++) { uint8_t aReg = (uint8_t)((instr[addr] >> 16) & 0xff); uint8_t bReg = (uint8_t)((instr[addr] >> 24) & 0xff); uint8_t cReg = (uint8_t)((instr[addr] >> 32) & 0xff); uint8_t dReg = (uint8_t)((instr[addr] >> 40) & 0xff); DESCRIBE_INSTR(buf, instr[addr], gpr[addr], name[aReg], name[bReg], name[cReg], name[dReg]); p("%s",string_format("%02x: %012I64x %06x (%8d) %s\n", addr, instr[addr], gpr[addr]&0xffffff, SX(gpr[addr]&0xffffff), buf).c_str()); } for (; addr < 0xc0; addr++) { p("%02x: %06x (%d)\n", addr, gpr[addr]&0xffffff, SX(gpr[addr]&0xffffff)); } } void es5510_device::execute_run() { while (icount > 0) { if (state == STATE_HALTED) { // Currently halted, sample the HALT line if (halt_asserted) { // remain halted host_control |= 0x04; // Signal Host Access OK } else { // start from the beginning at PC 0 state = STATE_RUNNING; host_control &= ~0x04; // Signal Host Access not OK pc = 0; } } else { // currently running, execute one instruction. #if VERBOSE_EXEC char buf[1024]; DESCRIBE_INSTR(buf, instr[pc], gpr[pc], nullptr, nullptr, nullptr, nullptr); LOG_EXEC(("%s",string_format("EXECUTING %02x: %012I64x %06x %s\n", pc, instr[pc], gpr[pc]&0xffffff, buf).c_str())); #endif ram_pp = ram_p; ram_p = ram; LOG_EXEC(("- T0\n")); // *** T0, clock high // --- nothing to do! // *** T0, clock low // --- Read instruction N uint64_t instr = this->instr[pc]; // --- RAM cycle N-2 (if a Read cycle): data read from bus is stored in DIL if (ram_pp.cycle != RAM_CYCLE_WRITE) { if (ram_pp.io) { // read from I/O and store into DIL dil = 0; // read_io(ram_pp.address);; } else { // read from DRAM and store into DIL dil = dram[ram_pp.address] << 8; LOG_EXEC((" . RAM: read %x (%d) from address %x\n", dil, dil, ram_pp.address)); } } // --- start of RAM cycle N ram_control_t ramControl = RAM_CONTROL[((instr >> 3) & 0x07)]; ram.cycle = ramControl.cycle; ram.io = ramControl.access == RAM_CONTROL_IO; // --- RAM cycle N: read offset N int32_t offset = gpr[pc]; switch(ramControl.access) { case RAM_CONTROL_DELAY: ram.address = (((dbase + offset) % (dlength + memincrement)) & memmask) >> memshift; LOG_EXEC((". Ram Control: Delay, base=%x, offset=%x, length=%x => address=%x\n", dbase >> memshift, offset >> memshift, (dlength + memincrement) >> memshift, ram.address)); break; case RAM_CONTROL_TABLE_A: ram.address = ((abase + offset) & memmask) >> memshift; LOG_EXEC((". Ram Control: table A = %x, offset=%x => address=%x\n", abase >> memshift, offset >> memshift, ram.address)); break; case RAM_CONTROL_TABLE_B: ram.address = ((bbase + offset) & memmask) >> memshift; LOG_EXEC((". Ram Control: table B = %x, offset=%x => address=%x\n", bbase >> memshift, offset >> memshift, ram.address)); break; case RAM_CONTROL_IO: ram.address = offset & 0x00fffff0; // mask off the low 4 bits LOG_EXEC((". Ram Control: I/O at address=%x\n", ram.address)); break; } // *** T1, clock high // --- Decode instruction N; // we will do this both here and in stages as the different parts of the instruction complete & recommence. LOG_EXEC(("- T1.1\n")); uint8_t operandSelect = (uint8_t)((instr >> 8) & 0x0f); const op_select_t &opSelect = OPERAND_SELECT[operandSelect]; bool skip; bool skippable = (instr & (0x01 << 7)) != 0; // aka the 'SKIP' bit in the instruction word if (skippable) { bool skipConditionSatisfied = (ccr & cmr & FLAG_MASK) != 0; if (isFlagSet(cmr, FLAG_NOT)) { skipConditionSatisfied = !skipConditionSatisfied; } skip = skipConditionSatisfied; LOG_EXEC((". skippable: %x vs %x => skippable = %d\n", ccr, cmr, skip)); } else { skip = false; } // --- Write Multiplier result N-1 LOG_EXEC((". write mulacc:\n")); if (mulacc.write_result) { mulacc.product = ((int64_t)SX(mulacc.cValue) * (int64_t)SX(mulacc.dValue)) << mulshift; if (mulacc.accumulate) { mulacc.result = mulacc.product + machl; } else { mulacc.result = mulacc.product; } if (mulacc.result < MIN_48 || mulacc.result > MAX_48) { mac_overflow = true; } else { mac_overflow = false; } #if VERBOSE_EXEC if (mulacc.cValue || mulacc.dValue || (mulacc.accumulate && machl)) { LOG_EXEC((". mulacc: %x (%d) * %x (%d) << %d", SX(mulacc.cValue), SX(mulacc.cValue), SX(mulacc.dValue), SX(mulacc.dValue), mulshift)); if (mulacc.accumulate) LOG_EXEC((" + %llx (%lld) ", machl, machl)); LOG_EXEC((" = %llx (%lld)", mulacc.result, mulacc.result)); if (mac_overflow) { LOG_EXEC((" overflow!\n")); } else { LOG_EXEC(("\n")); } } #endif machl = mulacc.result; int32_t tmp = mac_overflow ? (machl < 0 ? MIN_24 : MAX_24) : (mulacc.result & 0x0000ffffff000000U) >> 24; if (mulacc.dst & SRC_DST_REG) { write_reg(mulacc.cReg, tmp); } if (mulacc.dst & SRC_DST_DELAY) { write_to_dol(tmp); } } // *** T1, clock low LOG_EXEC(("- T1.0\n")); // --- Start of multiplier cycle N LOG_EXEC((". start mulacc:\n")); mulacc.cReg = (uint8_t)((instr >> 32) & 0xff); mulacc.dReg = (uint8_t)((instr >> 40) & 0xff); mulacc.src = opSelect.mac_src; mulacc.dst = opSelect.mac_dst; mulacc.accumulate = ((instr >> 6) & 0x01) != 0; mulacc.write_result = !skip; // --- Read Multiplier Operands N if (mulacc.src == SRC_DST_REG) { mulacc.cValue = read_reg(mulacc.cReg); } else { // must be SRC_DST_DELAY LOG_EXEC((" . reading %x (%d) from dil\n", dil, SX(dil))); mulacc.cValue = dil; } mulacc.dValue = read_reg(mulacc.dReg); // *** T2, clock high LOG_EXEC(("- T2.1\n")); // --- Write ALU Result N-1 LOG_EXEC((". write ALU:\n")); if (alu.write_result) { uint8_t flags = ccr; alu.result = alu_operation(alu.op, alu.aValue, alu.bValue, flags); if (alu.dst & SRC_DST_REG) { write_reg(alu.aReg, alu.result); } if (alu.dst & SRC_DST_DELAY) { write_to_dol(alu.result); } if (alu.update_ccr) { ccr = flags; } } // *** T2, clock low LOG_EXEC(("- T2.0\n")); // --- Start of ALU cycle N LOG_EXEC((". start ALU:\n")); alu.aReg = (instr >> 16) & 0xff; alu.bReg = (instr >> 24) & 0xff; alu.op = (instr >> 12) & 0x0f; alu.src = opSelect.alu_src; alu.dst = opSelect.alu_dst; alu.write_result = !skip; alu.update_ccr = !skippable || (alu.op == OP_CMP); if (alu.op == 0xF) { alu_operation_end(); } else { // --- Read ALU Operands N alu_op_t aluOp = ALU_OPS[alu.op]; if (aluOp.operands == 1) { if (alu.src == SRC_DST_REG) { alu.bValue = read_reg(alu.bReg); } else { // must be SRC_DST_DELAY alu.bValue = dil; } } else { if (alu.src == SRC_DST_REG) { alu.aValue = read_reg(alu.aReg); } else { // must be SRC_DST_DELAY alu.aValue = dil; } alu.bValue = read_reg(alu.bReg); } } // --- RAM cycle N-1 if (ram_p.cycle != RAM_CYCLE_READ) { if (ram_p.cycle == RAM_CYCLE_WRITE) { // If this is a write cycle, write the frontmost DOL value to RAM or I/O if (ram_p.io) { // write_io(ram_p.io, dol[0]); } else { dram[ram_p.address] = dol[0] >> 8; LOG_EXEC((" . RAM: writing %x (%d) [of %x (%d)] to address %x\n", dol[0]&0xffff00, SX(dol[0]&0xffff00), dol[0], SX(dol[0]), ram_p.address)); } } // If this is a Write or Dump cycle, eject the frontmost DL value. #if VERBOSE_EXEC LOG_EXEC((" . ejecting from DOL: [ ")); if (dol_count >= 1) LOG_EXEC(("{ %x (%d) }", dol[0], SX(dol[0]))); if (dol_count == 2) LOG_EXEC((", { %x (%d) }", dol[1], SX(dol[1]))); LOG_EXEC((" ] -> [ ")); #endif dol[0] = dol[1]; if (dol_count > 0) { --dol_count; } #if VERBOSE_EXEC if (dol_count >= 1) LOG_EXEC(("{ %x (%d) }", dol[0], SX(dol[0]))); if (dol_count == 2) LOG_EXEC((", { %x (%d) }", dol[1], SX(dol[1]))); LOG_EXEC((" ]\n")); #endif } ++pc; } --icount; } } uint32_t es5510_device::disasm_min_opcode_bytes() const { return 6; } uint32_t es5510_device::disasm_max_opcode_bytes() const { return 6; } offs_t es5510_device::disasm_disassemble(std::ostream &stream, offs_t pc, const uint8_t *oprom, const uint8_t *opram, uint32_t options) { return pc; } #if VERBOSE_EXEC #define RETURN_GPR(r, x) do { int32_t v = (x); LOG_EXEC((" . reading %x (%d) from gpr_%02x\n", v, SX(v), r)); return v; } while(0) #define RETURN(r, x) do { int32_t v = (x); LOG_EXEC((" . reading %x (%d) from " #r "\n", v, SX(v))); return v; } while(0) #define RETURN16(r, x) do { int16_t vv = (x); int32_t v = vv << 8; LOG_EXEC((" . reading %x (%d) as %x (%d) from " #r "\n", vv, vv, v, SX(v))); return v; } while(0) #else #define RETURN_GPR(r, x) return x #define RETURN(r, x) return x #define RETURN16(r, x) return (x) << 8 #endif int32_t es5510_device::read_reg(uint8_t reg) { if (reg < 0xc0) { RETURN_GPR(reg, gpr[reg]); } else { switch(reg) { case 234: RETURN16(ser0r, ser0r); case 235: RETURN16(ser0l, ser0l); case 236: RETURN16(ser1r, ser1r); case 237: RETURN16(ser1l, ser1l); case 238: RETURN16(ser2r, ser2r); case 239: RETURN16(ser2l, ser2l); case 240: RETURN16(ser3r, ser3r); case 241: RETURN16(ser3l, ser3l); case 242: /* macl */ RETURN(macl, mac_overflow ? (machl < 0 ? 0x00ffffff : 0x00000000) : (machl >> 0) & 0x00ffffff); case 243: /* mach */ RETURN(mach, mac_overflow ? (machl < 0 ? MIN_24 : MAX_24) : (machl >> 24) & 0x00ffffff); case 244: RETURN(dil, dil); // DIL when reading case 245: RETURN(dlength, dlength); case 246: RETURN(abase, abase); case 247: RETURN(bbase, bbase); case 248: RETURN(dbase, dbase); case 249: RETURN(sigreg, sigreg); case 250: RETURN(ccr, ccr); case 251: RETURN(cmr, cmr); case 252: RETURN(minus_one, 0x00ffffff); case 253: RETURN(min, MIN_24); case 254: RETURN(max, MAX_24); case 255: RETURN(zero, 0); default: // unknown SPR RETURN(unknown, 0); } } } void es5510_device::run_once() { // turn HALT off set_HALT(false); // run for one instruction icount = 1; execute_run(); // turn HALT on again set_HALT(true); // run ESP to the end of its program, a few instructions at a time while (state != STATE_HALTED) { icount = 1; execute_run(); } } int8_t countLowOnes(int32_t x) { int8_t n = 0; while ((x & 1) == 1) { ++n; x >>= 1; } return n; } #if VERBOSE_EXEC #define WRITE_REG(r, x) do { r = value; LOG_EXEC((" . writing %x (%d) to " #r "\n", r, SX(r))); } while(0) #define WRITE_REG16(r, x) do { r = ((value >> 8) & 0xffff); LOG_EXEC((" . writing %x (%d) as %x (%d) to " #r "\n", value, SX(value), r, r)); } while(0) #else #define WRITE_REG(r, x) do { r = value; } while(0) #define WRITE_REG16(r, x) do { r = ((value >> 8) & 0xffff); } while(0) #endif void es5510_device::write_reg(uint8_t reg, int32_t value) { #if VERBOSE_EXEC int64_t old; #endif value &= 0x00ffffff; if (reg < 0xc0) { LOG_EXEC((" . writing %x (%d) to gpr_%02x\n", value, SX(value), reg)); gpr[reg] = value; } else { switch(reg) { case 234: WRITE_REG16(ser0r, value); break; case 235: WRITE_REG16(ser0l, value); break; case 236: WRITE_REG16(ser1r, value); break; case 237: WRITE_REG16(ser1l, value); break; case 238: WRITE_REG16(ser2r, value); break; case 239: WRITE_REG16(ser2l, value); break; case 240: WRITE_REG16(ser3r, value); break; case 241: WRITE_REG16(ser3l, value); break; case 242: /* macl */ { #if VERBOSE_EXEC old = machl; #endif int64_t masked = machl & (s64(0x00ffffffU) << 24); int64_t shifted = (int64_t)(value & 0x00ffffff) << 0; machl = SX64(masked | shifted); #if VERBOSE_EXEC LOG_EXEC((" . writing machl: l -> %06x => %llx -> %llx\n", value, old, machl)); #endif break; } case 243: /* mach */ { #if VERBOSE_EXEC old = machl; #endif int64_t masked = machl & (s64(0x00ffffffU) << 0); int64_t shifted = (int64_t)(value & 0x00ffffff) << 24; machl = SX64(masked | shifted); mac_overflow = false; #if VERBOSE_EXEC LOG_EXEC((" . writing machl: h -> %06x => %llx -> %llx\n", value, old, machl)); #endif break; } case 244: /* MEMSIZ when writing */ memshift = countLowOnes(value); memsiz = 0x00ffffff >> (24 - memshift); memmask = 0x00ffffff & ~memsiz; memincrement = 1 << memshift; LOG_EXEC((" . writing %x (%d) to memsiz => memsiz=%x, shift=%d, mask=%x, increment=%x\n", value, SX(value), memsiz, memshift, memmask, memincrement)); break; case 245: WRITE_REG(dlength, value); break; case 246: WRITE_REG(abase, value); break; case 247: WRITE_REG(bbase, value); break; case 248: WRITE_REG(dbase, value); break; case 249: WRITE_REG(sigreg, (value != 0)); break; case 250: WRITE_REG(ccr, (value >> 16) & FLAG_MASK); break; case 251: WRITE_REG(cmr, (value >> 16) & (FLAG_MASK | FLAG_NOT)); break; case 252: LOG_EXEC((". not writing %x (%d) to minus_one\n", value, SX(value))); // no-op break; case 253: LOG_EXEC((". not writing %x (%d) to min\n", value, SX(value))); // no-op break; case 254: LOG_EXEC((". not writing %x (%d) to max\n", value, SX(value))); // no-op break; case 255: LOG_EXEC((". not writing %x (%d) to zero\n", value, SX(value))); // no-op break; default: // unknown register break; } } } void es5510_device::write_to_dol(int32_t value) { #if VERBOSE_EXEC LOG_EXEC((". writing %x (%d) to DOL: [ ", value, value)); if (dol_count >= 1) LOG_EXEC(("{ %x (%d) }", dol[0], SX(dol[0]))); if (dol_count == 2) LOG_EXEC((", { %x (%d) }", dol[1], SX(dol[1]))); LOG_EXEC((" ] -> [ ")); #endif if (dol_count >= 2) { dol[0] = dol[1]; dol[1] = value; } else { dol[dol_count++] = value; } #if VERBOSE_EXEC LOG_EXEC(("{%x (%d)}", dol[0], SX(dol[0]))); if (dol_count == 2) LOG_EXEC((", {%x (%d)}", dol[1], SX(dol[1]))); LOG_EXEC((" ]\n")); #endif } void es5510_device::alu_operation_end() { // Handle the END instruction separately LOG_EXEC(("ES5510: END\n")); // sample the HALT line if (halt_asserted) { // halt state = STATE_HALTED; host_control |= 0x04; // Signal Host Access OK } // update the delay line base pointer dbase -= memincrement; if (dbase < 0) { dbase = dlength; } // Possibly reset the PC if (state == STATE_RUNNING) { pc = 0; } #if VERBOSE_EXEC // update the verbose-execution counter. exec_cc = (exec_cc + 1) % 30000; #endif } int32_t es5510_device::alu_operation(uint8_t op, int32_t a, int32_t b, uint8_t &flags) { int32_t tmp; switch(op) { case 0x0: // ADD tmp = add(a, b, flags); return saturate(tmp, flags, (a & 0x00800000) != 0); case 0x1: // SUB tmp = add(a, negate(b), flags); return saturate(tmp, flags, (a & 0x00800000) != 0); case 0x2: // ADDU return add(a, b, flags); case 0x3: // SUBU return add(a, negate(b), flags); case 0x4: // CMP // perform the subtraction, only for its effect on the flags add(a, negate(b), flags); return a; case 0x5: // AND a &= b; setFlagTo(flags, FLAG_N, (a & 0x0080000000) != 0); setFlagTo(flags, FLAG_Z, a == 0); return a; case 0x6: // OR a |= b; setFlagTo(flags, FLAG_N, (a & 0x0080000000) != 0); setFlagTo(flags, FLAG_Z, a == 0); return a; case 0x7: // XOR a ^= b; setFlagTo(flags, FLAG_N, (a & 0x0080000000) != 0); setFlagTo(flags, FLAG_Z, a == 0); return a; case 0x8: // ABS { clearFlag(flags, FLAG_N); bool isNegative = (a & 0x00800000) != 0; setFlagTo(flags, FLAG_C, isNegative); // Note: the absolute value is calculated by one's complement! return isNegative ? (0x00ffffff ^ a) : a; } case 0x9: // MOV return b; case 0xA: // ASL2 return asl(b, 2, flags); case 0xB: // ASL8 return asl(b, 8, flags); case 0xC: // LS15 return (b << 15) & 0x007fffff; case 0xD: // DIFF return add(0x007fffff, negate(b), flags); case 0xE: // ASR return (b >> 1) | (b & 0x00800000); case 0xF: // END - handled separately in alu_operation_end() default: return 0; } }