diff --git a/src/devices/cpu/clipper/clipper.cpp b/src/devices/cpu/clipper/clipper.cpp new file mode 100644 index 00000000000..be80487c041 --- /dev/null +++ b/src/devices/cpu/clipper/clipper.cpp @@ -0,0 +1,871 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#include "emu.h" +#include "debugger.h" +#include "clipper.h" + +#define SSP_HACK 0 + +#define VERBOSE 0 +#if VERBOSE +#define LOG_INTERRUPT(...) logerror(__VA_ARGS__) +#else +#define LOG_INTERRUPT(...) +#endif + + +const device_type CLIPPER = &device_creator; + +clipper_device::clipper_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : cpu_device(mconfig, CLIPPER, "c400", tag, owner, clock, "c400", __FILE__), + m_program_config("program", ENDIANNESS_LITTLE, 32, 32, 0), + m_program(nullptr), + m_direct(nullptr), + m_pc(0), + m_icount(0), + m_interrupt_cycles(0) +{ +} + +void clipper_device::device_start() +{ + // get our address spaces + m_program = &space(AS_PROGRAM); + m_direct = &m_program->direct(); + + // set our instruction counter + m_icountptr = &m_icount; + + //save_item(NAME(m_pc)); + + state_add(STATE_GENPC, "GENPC", m_pc).noshow(); + state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow(); + + state_add(CLIPPER_PC, "pc", m_pc).formatstr("%08X"); + state_add(CLIPPER_PSW, "psw", m_psw.d).formatstr("%08X"); + state_add(CLIPPER_SSW, "ssw", m_ssw.d).formatstr("%08X"); + + state_add(CLIPPER_R0, "r0", m_r[m_ssw.bits.u][0]).formatstr("%08X"); + state_add(CLIPPER_R1, "r1", m_r[m_ssw.bits.u][1]).formatstr("%08X"); + state_add(CLIPPER_R2, "r2", m_r[m_ssw.bits.u][2]).formatstr("%08X"); + state_add(CLIPPER_R3, "r3", m_r[m_ssw.bits.u][3]).formatstr("%08X"); + state_add(CLIPPER_R4, "r4", m_r[m_ssw.bits.u][4]).formatstr("%08X"); + state_add(CLIPPER_R5, "r5", m_r[m_ssw.bits.u][5]).formatstr("%08X"); + state_add(CLIPPER_R6, "r6", m_r[m_ssw.bits.u][6]).formatstr("%08X"); + state_add(CLIPPER_R7, "r7", m_r[m_ssw.bits.u][7]).formatstr("%08X"); + state_add(CLIPPER_R8, "r8", m_r[m_ssw.bits.u][8]).formatstr("%08X"); + state_add(CLIPPER_R9, "r9", m_r[m_ssw.bits.u][9]).formatstr("%08X"); + state_add(CLIPPER_R10, "r10", m_r[m_ssw.bits.u][10]).formatstr("%08X"); + state_add(CLIPPER_R11, "r11", m_r[m_ssw.bits.u][11]).formatstr("%08X"); + state_add(CLIPPER_R12, "r12", m_r[m_ssw.bits.u][12]).formatstr("%08X"); + state_add(CLIPPER_R13, "r13", m_r[m_ssw.bits.u][13]).formatstr("%08X"); + state_add(CLIPPER_R14, "r14", m_r[m_ssw.bits.u][14]).formatstr("%08X"); + state_add(CLIPPER_R15, "r15", m_r[m_ssw.bits.u][15]).formatstr("%08X"); + + state_add(CLIPPER_F0, "f0", m_f[0]).formatstr("%016X"); + state_add(CLIPPER_F1, "f1", m_f[1]).formatstr("%016X"); + state_add(CLIPPER_F2, "f2", m_f[2]).formatstr("%016X"); + state_add(CLIPPER_F3, "f3", m_f[3]).formatstr("%016X"); + state_add(CLIPPER_F4, "f4", m_f[4]).formatstr("%016X"); + state_add(CLIPPER_F5, "f5", m_f[5]).formatstr("%016X"); + state_add(CLIPPER_F6, "f6", m_f[6]).formatstr("%016X"); + state_add(CLIPPER_F7, "f7", m_f[7]).formatstr("%016X"); + state_add(CLIPPER_F8, "f8", m_f[8]).formatstr("%016X"); + state_add(CLIPPER_F9, "f9", m_f[9]).formatstr("%016X"); + state_add(CLIPPER_F10, "f10", m_f[10]).formatstr("%016X"); + state_add(CLIPPER_F11, "f11", m_f[11]).formatstr("%016X"); + state_add(CLIPPER_F12, "f12", m_f[12]).formatstr("%016X"); + state_add(CLIPPER_F13, "f13", m_f[13]).formatstr("%016X"); + state_add(CLIPPER_F14, "f14", m_f[14]).formatstr("%016X"); + state_add(CLIPPER_F15, "f15", m_f[15]).formatstr("%016X"); +} + +void clipper_device::device_reset() +{ + m_ssw.bits.m = 0; // FIXME: turn off mapping + m_ssw.bits.u = 0; + + m_pc = 0x7F100000; // FIXME: start executing boot rom + m_immediate_irq = 0; +} + +void clipper_device::state_string_export(const device_state_entry &entry, std::string &str) const +{ + switch (entry.index()) + { + default: + break; + } +} + +void clipper_device::execute_run() +{ + uint16_t insn; + + if (m_immediate_irq) + { + LOG_INTERRUPT("taking interrupt - current pc = %08x\n", m_pc); + + m_pc = intrap(0x800 + m_immediate_vector * 8, m_pc); + m_immediate_irq = 0; + } + + while (m_icount > 0) { + + debugger_instruction_hook(this, m_pc); + + // fetch instruction word + insn = m_direct->read_word(m_pc + 0); + + // decode and execute instruction, return next pc + m_pc = execute_instruction(insn); + + m_icount--; + } +} + +void clipper_device::execute_set_input(int inputnum, int state) +{ + if (state) + { + // clock is vector 0e29 0000 1110 + // floppy is vector 0621 0000 0110 + + uint16_t vector = standard_irq_callback(inputnum); + + uint8_t ivec = vector & 0xff; + uint8_t il = ivec >> 4; + uint8_t in = ivec & 0xf; + + LOG_INTERRUPT("received interrupt with vector %04x\n", vector); + + // allow NMI or equal/higher priority interrupts + if (ivec == 0 || (m_ssw.bits.ei && (il <= m_ssw.bits.il))) + { + m_immediate_irq = 1; + m_immediate_vector = ivec; + + LOG_INTERRUPT("accepting interrupt %x\n", m_immediate_vector); + } + } +} + +const address_space_config * clipper_device::memory_space_config(address_spacenum spacenum) const +{ + return (spacenum == AS_PROGRAM) ? &m_program_config : nullptr; +} + +#define R1 ((insn & 0x00F0) >> 4) +#define R2 (insn & 0x000F) +int clipper_device::execute_instruction(uint16_t insn) +{ + uint32_t next_pc = 0; + union { + int32_t imm; // used by instructions with immediate operand values + uint32_t r2; // used by instructions with complex addressing modes + uint16_t macro; // used by macro instructions + } op; + op.imm = op.r2 = op.macro = 0; + uint32_t addr_ea = 0; // used by instructions with complex addressing modes + + // test for immediate, address or macro instructions and decode additional operands + if ((insn & 0xF800) == 0x3800 || (insn & 0xD300) == 0x8300) + { + // immediate instructions + if (insn & 0x0080) + { + // fetch 16 bit immediate and sign extend + op.imm = (int16_t)m_direct->read_word(m_pc + 2); + next_pc = m_pc + 4; + } + else + { + // fetch 32 bit immediate and sign extend + op.imm = (int32_t)m_direct->read_dword(m_pc + 2); + next_pc = m_pc + 6; + } + } + else if ((insn & 0xF100) == 0x4100 || (insn & 0xE100) == 0x6100) + { + // instructions with complex addressing modes (b*, bf*, call, load*, stor*) + uint16_t temp; + + switch (insn & 0x00F0) + { + case ADDR_MODE_PC32: + op.r2 = R2; + addr_ea = m_pc + (int32_t)m_direct->read_dword(m_pc + 2); + next_pc = m_pc + 6; + break; + + case ADDR_MODE_ABS32: + op.r2 = R2; + addr_ea = m_direct->read_dword(m_pc + 2); + next_pc = m_pc + 6; + break; + + case ADDR_MODE_REL32: + op.r2 = m_direct->read_word(m_pc + 2) & 0xf; + addr_ea = m_r[m_ssw.bits.u][R2] + (int32_t)m_direct->read_dword(m_pc + 4); + next_pc = m_pc + 8; + break; + + case ADDR_MODE_PC16: + op.r2 = R2; + addr_ea = m_pc + (int16_t)m_direct->read_word(m_pc + 2); + next_pc = m_pc + 4; + break; + + case ADDR_MODE_REL12: + temp = m_direct->read_word(m_pc + 2); + + op.r2 = temp & 0xf; + addr_ea = m_r[m_ssw.bits.u][R2] + ((int16_t)temp >> 4); + next_pc = m_pc + 4; + break; + + case ADDR_MODE_ABS16: + op.r2 = R2; + addr_ea = (int16_t)m_direct->read_word(m_pc + 2); + next_pc = m_pc + 4; + break; + + case ADDR_MODE_PCX: + temp = m_direct->read_word(m_pc + 2); + + op.r2 = temp & 0xf; + addr_ea = m_pc + m_r[m_ssw.bits.u][(temp >> 4) & 0xf]; + next_pc = m_pc + 4; + break; + + case ADDR_MODE_RELX: + temp = m_direct->read_word(m_pc + 2); + + op.r2 = temp & 0xf; + addr_ea = m_r[m_ssw.bits.u][R2] + m_r[m_ssw.bits.u][(temp >> 4) & 0xf]; + next_pc = m_pc + 4; + break; + + default: + logerror("illegal addressing mode pc = 0x%08x\n", m_pc); + machine().debug_break(); + break; + } + } + else if ((insn & 0xFD00) == 0xB400) + { + // macro instructions + op.macro = m_direct->read_word(m_pc + 2); + next_pc = m_pc + 4; + } + else + next_pc = m_pc + 2; + + switch (insn >> 8) + { + case 0x00: // noop + break; + + case 0x10: + // movwp (treated as a noop if target ssw in user mode) + // R1 == 3 means "fast" mode - avoids pipeline flush + if (R1 == 0) + m_psw.d = m_r[m_ssw.bits.u][R2]; + else if (m_ssw.bits.u == 0 && (R1 == 1 || R1 == 3)) + m_ssw.d = m_r[m_ssw.bits.u][R2]; + break; + + case 0x11: // movpw + switch (R1) + { + case 0: m_r[m_ssw.bits.u][R2] = m_psw.d; break; + case 1: m_r[m_ssw.bits.u][R2] = m_ssw.d; break; + } + break; + + case 0x12: // calls + next_pc = intrap(0x400 + (insn & 0x7F) * 8, next_pc); + break; + + case 0x13: // ret + next_pc = m_program->read_dword(m_r[m_ssw.bits.u][R2]); + m_r[m_ssw.bits.u][R2] += 4; + break; + + case 0x14: // pushw + m_r[m_ssw.bits.u][R1] -= 4; + m_program->write_dword(m_r[m_ssw.bits.u][R1], m_r[m_ssw.bits.u][R2]); + break; + + case 0x16: // popw + m_r[m_ssw.bits.u][R2] = m_program->read_dword(m_r[m_ssw.bits.u][R1]); + m_r[m_ssw.bits.u][R1] += 4; + break; + + case 0x20: *((float *)&m_f[R2]) -= *((float *)&m_f[R1]); break; // subs + case 0x21: *((float *)&m_f[R2]) += *((float *)&m_f[R1]); break; // adds + case 0x22: m_f[R2] += m_f[R1]; break; // addd + case 0x23: m_f[R2] -= m_f[R1]; break; // subd + case 0x24: *((float *)&m_f[R2]) = *((float *)&m_f[R1]); break; // movs + case 0x25: evaluate_cc2f(*((float *)&m_f[R2]), *((float *)&m_f[R1])); break; // cmps + case 0x26: m_f[R2] = m_f[R1]; break; // movd + case 0x27: evaluate_cc2f(m_f[R2], m_f[R1]); break; // cmpd + case 0x2A: m_f[R2] *= m_f[R1]; break; // muld + case 0x2B: m_f[R2] /= m_f[R1]; break; // divd + case 0x2C: m_r[m_ssw.bits.u][R2] = *((int32_t *)&m_f[R1]); break; // movsw + case 0x2D: *((int32_t *)&m_f[R2]) = m_r[m_ssw.bits.u][R1]; break; // movws + case 0x2E: ((double *)m_r[m_ssw.bits.u])[R2 >> 1] = m_f[R1]; break; // movdl + case 0x2F: m_f[R2] = ((double *)m_r[m_ssw.bits.u])[R1 >> 1]; break; // movld + + case 0x30: // shaw + if ((int32_t)m_r[m_ssw.bits.u][R1] > 0) + m_r[m_ssw.bits.u][R2] = (int32_t)m_r[m_ssw.bits.u][R2] << m_r[m_ssw.bits.u][R1]; + else + m_r[m_ssw.bits.u][R2] = (int32_t)m_r[m_ssw.bits.u][R2] >> -(int32_t)m_r[m_ssw.bits.u][R1]; + break; + case 0x31: // shal + if ((int32_t)m_r[m_ssw.bits.u][R1] > 0) + ((int64_t *)m_r[m_ssw.bits.u])[R2 >> 1] <<= m_r[m_ssw.bits.u][R1]; + else + ((int64_t *)m_r[m_ssw.bits.u])[R2 >> 1] >>= -(int32_t)m_r[m_ssw.bits.u][R1]; + break; + case 0x32: // shlw + if ((int32_t)m_r[m_ssw.bits.u][R1] > 0) + m_r[m_ssw.bits.u][R2] <<= m_r[m_ssw.bits.u][R1]; + else + m_r[m_ssw.bits.u][R2] >>= -(int32_t)m_r[m_ssw.bits.u][R1]; + break; + case 0x33: // shll + if ((int32_t)m_r[m_ssw.bits.u][R1] > 0) + ((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1] <<= m_r[m_ssw.bits.u][R1]; + else + ((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1] >>= -(int32_t)m_r[m_ssw.bits.u][R1]; + break; + case 0x34: // rotw + if ((int32_t)m_r[m_ssw.bits.u][R1] > 0) + m_r[m_ssw.bits.u][R2] = _rotl(m_r[m_ssw.bits.u][R2], m_r[m_ssw.bits.u][R1]); + else + m_r[m_ssw.bits.u][R2] = _rotr(m_r[m_ssw.bits.u][R2], -(int32_t)m_r[m_ssw.bits.u][R1]); + break; + case 0x35: // rotl + if ((int32_t)m_r[m_ssw.bits.u][R1] > 0) + ((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1] = _rotl64(((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1], m_r[m_ssw.bits.u][R1]); + else + ((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1] = _rotr64(((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1], -(int32_t)m_r[m_ssw.bits.u][R1]); + break; + case 0x38: // shai + if (op.imm > 0) + m_r[m_ssw.bits.u][R2] = (int32_t)m_r[m_ssw.bits.u][R2] << op.imm; + else + m_r[m_ssw.bits.u][R2] = (int32_t)m_r[m_ssw.bits.u][R2] >> -op.imm; + break; + case 0x39: // shali + if (op.imm > 0) + ((int64_t *)m_r[m_ssw.bits.u])[R2 >> 1] <<= op.imm; + else + ((int64_t *)m_r[m_ssw.bits.u])[R2 >> 1] >>= -op.imm; + break; + case 0x3A: // shli + if (op.imm > 0) + m_r[m_ssw.bits.u][R2] <<= op.imm; + else + m_r[m_ssw.bits.u][R2] >>= -op.imm; + break; + case 0x3B: // shlli + if (op.imm > 0) + ((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1] <<= op.imm; + else + ((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1] >>= -op.imm; + break; + case 0x3C: // roti + if (op.imm > 0) + m_r[m_ssw.bits.u][R2] = _rotl(m_r[m_ssw.bits.u][R2], op.imm); + else + m_r[m_ssw.bits.u][R2] = _rotr(m_r[m_ssw.bits.u][R2], -op.imm); + break; + case 0x3D: // rotli + if (op.imm > 0) + ((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1] = _rotl64(((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1], op.imm); + else + ((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1] = _rotr64(((uint64_t *)m_r[m_ssw.bits.u])[R2 >> 1], -op.imm); + break; + + case 0x44: // call + m_r[m_ssw.bits.u][R2] -= 4; // decrement sp + m_program->write_dword(m_r[m_ssw.bits.u][R2], next_pc); // push return address + next_pc = m_r[m_ssw.bits.u][R1]; + break; + case 0x45: // call + m_r[m_ssw.bits.u][op.r2] -= 4; // decrement sp + m_program->write_dword(m_r[m_ssw.bits.u][op.r2], next_pc); // push return address + next_pc = addr_ea; + break; + + case 0x48: // b* + if (evaluate_branch(R2)) + next_pc = m_r[m_ssw.bits.u][R1]; + break; + case 0x49: // b* + if (evaluate_branch(op.r2)) + next_pc = addr_ea; + break; + + case 0x60: // loadw + m_r[m_ssw.bits.u][R2] = m_program->read_dword(m_r[m_ssw.bits.u][R1]); + break; + case 0x61: // loadw + m_r[m_ssw.bits.u][op.r2] = m_program->read_dword(addr_ea); + break; + + case 0x62: // loada + m_r[m_ssw.bits.u][R2] = m_r[m_ssw.bits.u][R1]; + break; + case 0x63: + m_r[m_ssw.bits.u][op.r2] = addr_ea; + break; + + case 0x64: // loads + ((uint64_t *)&m_f)[R2] = m_program->read_dword(m_r[m_ssw.bits.u][R1]); + break; + case 0x65: // loads + ((uint64_t *)&m_f)[op.r2] = m_program->read_dword(addr_ea); + break; + + case 0x66: // loadd + ((uint64_t *)&m_f)[R2] = m_program->read_qword(m_r[m_ssw.bits.u][R1]); + break; + case 0x67: // loadd + ((uint64_t *)&m_f)[op.r2] = m_program->read_qword(addr_ea); + break; + + case 0x68: // loadb + m_r[m_ssw.bits.u][R2] = (int8_t)m_program->read_byte(m_r[m_ssw.bits.u][R1]); + break; + case 0x69: // loadb + m_r[m_ssw.bits.u][op.r2] = (int8_t)m_program->read_byte(addr_ea); + break; + + case 0x6A: // loadbu + m_r[m_ssw.bits.u][R2] = (uint8_t)m_program->read_byte(m_r[m_ssw.bits.u][R1]); + break; + case 0x6B: + m_r[m_ssw.bits.u][op.r2] = m_program->read_byte(addr_ea); + break; + + case 0x6C: // loadh + m_r[m_ssw.bits.u][R2] = (int16_t)m_program->read_word(m_r[m_ssw.bits.u][R1]); + break; + case 0x6D: + m_r[m_ssw.bits.u][op.r2] = (int16_t)m_program->read_word(addr_ea); + break; + + case 0x6E: // loadhu + m_r[m_ssw.bits.u][R2] = (uint16_t)m_program->read_word(m_r[m_ssw.bits.u][R1]); + break; + case 0x6F: // loadhu + m_r[m_ssw.bits.u][op.r2] = m_program->read_word(addr_ea); + break; + + case 0x70: // storw + m_program->write_dword(m_r[m_ssw.bits.u][R1], m_r[m_ssw.bits.u][R2]); + break; + case 0x71: + m_program->write_dword(addr_ea, m_r[m_ssw.bits.u][op.r2]); + break; + + case 0x74: // stors + m_program->write_dword(m_r[m_ssw.bits.u][R1], *((uint32_t *)&m_f[R2])); + break; + case 0x75: // stors + m_program->write_dword(addr_ea, *((uint32_t *)&m_f[op.r2])); + break; + + case 0x76: // stord + m_program->write_qword(m_r[m_ssw.bits.u][R1], *((uint64_t *)&m_f[R2])); + break; + case 0x77: // stord + m_program->write_qword(addr_ea, *((uint64_t *)&m_f[op.r2])); + break; + + case 0x78: // storb + m_program->write_byte(m_r[m_ssw.bits.u][R1], (uint8_t)m_r[m_ssw.bits.u][R2]); + break; + case 0x79: + m_program->write_byte(addr_ea, (uint8_t)m_r[m_ssw.bits.u][op.r2]); + break; + + case 0x7C: // storh + m_program->write_word(m_r[m_ssw.bits.u][R1], (uint16_t)m_r[m_ssw.bits.u][R2]); + break; + case 0x7D: + m_program->write_word(addr_ea, (uint16_t)m_r[m_ssw.bits.u][op.r2]); + break; + + case 0x80: // addw + m_r[m_ssw.bits.u][R2] += m_r[m_ssw.bits.u][R1]; + break; + + case 0x82: // addq + m_r[m_ssw.bits.u][R2] += R1; + break; + case 0x83: // addi + m_r[m_ssw.bits.u][R2] += op.imm; + break; + case 0x84: // movw + m_r[m_ssw.bits.u][R2] = m_r[m_ssw.bits.u][R1]; + evaluate_cc1(m_r[m_ssw.bits.u][R2]); + break; + + case 0x86: // loadq + m_r[m_ssw.bits.u][R2] = R1; + break; + case 0x87: // loadi + m_r[m_ssw.bits.u][R2] = op.imm; + break; + case 0x88: // andw + m_r[m_ssw.bits.u][R2] &= m_r[m_ssw.bits.u][R1]; + evaluate_cc1(m_r[m_ssw.bits.u][R2]); + break; + + case 0x8B: // andi + m_r[m_ssw.bits.u][R2] &= op.imm; + evaluate_cc1(m_r[m_ssw.bits.u][R2]); + break; + case 0x8C: // orw + m_r[m_ssw.bits.u][R2] |= m_r[m_ssw.bits.u][R1]; + evaluate_cc1(m_r[m_ssw.bits.u][R2]); + break; + + case 0x8F: // ori + m_r[m_ssw.bits.u][R2] |= op.imm; + evaluate_cc1(m_r[m_ssw.bits.u][R2]); + break; + +#if 0 + case 0x90: // addwc + m_r[m_ssw.bits.u][r2] += m_r[m_ssw.bits.u][r1] + psw.b.c; + break; + + case 0x91: // subwc + m_r[m_ssw.bits.u][r2] -= m_r[m_ssw.bits.u][r1] + psw.b.c; + break; +#endif + + case 0x93: // negw + m_r[m_ssw.bits.u][R2] = -(int32_t)m_r[m_ssw.bits.u][R1]; + break; + + case 0x98: // mulw + m_r[m_ssw.bits.u][R2] = (int32_t)m_r[m_ssw.bits.u][R2] * (int32_t)m_r[m_ssw.bits.u][R1]; + break; + // TODO: mulwx + case 0x9A: // mulwu + m_r[m_ssw.bits.u][R2] = (uint32_t)m_r[m_ssw.bits.u][R2] * (uint32_t)m_r[m_ssw.bits.u][R1]; + break; + // TODO: mulwux + case 0x9C: // divw + m_r[m_ssw.bits.u][R2] = (int32_t)m_r[m_ssw.bits.u][R2] / (int32_t)m_r[m_ssw.bits.u][R1]; + break; + case 0x9D: // modw + m_r[m_ssw.bits.u][R2] = (int32_t)m_r[m_ssw.bits.u][R2] % (int32_t)m_r[m_ssw.bits.u][R1]; + break; + case 0x9E: // divwu + m_r[m_ssw.bits.u][R2] = (uint32_t)m_r[m_ssw.bits.u][R2] / (uint32_t)m_r[m_ssw.bits.u][R1]; + break; + case 0x9F: // modwu + m_r[m_ssw.bits.u][R2] = (uint32_t)m_r[m_ssw.bits.u][R2] % (uint32_t)m_r[m_ssw.bits.u][R1]; + break; + + case 0xA0: // subw + evaluate_cc2(m_r[m_ssw.bits.u][R2], m_r[m_ssw.bits.u][R1]); + m_r[m_ssw.bits.u][R2] -= m_r[m_ssw.bits.u][R1]; + break; + + case 0xA2: // subq + evaluate_cc2(m_r[m_ssw.bits.u][R2], R1); + m_r[m_ssw.bits.u][R2] -= R1; + break; + case 0xA3: // subi + evaluate_cc2(m_r[m_ssw.bits.u][R2], op.imm); + m_r[m_ssw.bits.u][R2] -= op.imm; + break; + case 0xA4: // cmpw + evaluate_cc2(m_r[m_ssw.bits.u][R2], m_r[m_ssw.bits.u][R1]); + break; + + case 0xA6: // cmpq + evaluate_cc2(m_r[m_ssw.bits.u][R2], R1); + break; + case 0xA7: // cmpi + evaluate_cc2(m_r[m_ssw.bits.u][R2], op.imm); + break; + case 0xA8: // xorw + m_r[m_ssw.bits.u][R2] ^= m_r[m_ssw.bits.u][R1]; + evaluate_cc1(m_r[m_ssw.bits.u][R2]); + break; + + case 0xAB: // xori + m_r[m_ssw.bits.u][R2] ^= op.imm; + evaluate_cc1(m_r[m_ssw.bits.u][R2]); + break; + case 0xAC: // notw + m_r[m_ssw.bits.u][R2] = ~m_r[m_ssw.bits.u][R1]; + evaluate_cc1(m_r[m_ssw.bits.u][R2]); + break; + + case 0xAE: // notq + m_r[m_ssw.bits.u][R2] = ~R1; + evaluate_cc1(m_r[m_ssw.bits.u][R2]); + break; + + case 0xB4: + switch (insn & 0xFF) + { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: + // savew0..savew12: push registers from rN through r14 + + // store ri at sp - 4 * (15 - i) + for (int reg = R2; reg < 15; reg++) + m_program->write_dword(m_r[m_ssw.bits.u][15] - 4 * (15 - reg), m_r[m_ssw.bits.u][reg]); + + // decrement sp after push to allow restart on exceptions + m_r[m_ssw.bits.u][15] -= 4 * (15 - R2); + break; + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: + case 0x1C: + // restwN..restw12: pop registers from rN:r14 + + // load ri from sp + 4 * (i - N) + for (int reg = R2; reg < 15; reg++) + m_r[m_ssw.bits.u][reg] = m_program->read_dword(m_r[m_ssw.bits.u][15] + 4 * (reg - R2)); + + // increment sp after pop to allow restart on exceptions + m_r[m_ssw.bits.u][15] += 4 * (15 - R2); + break; + + case 0x36: // cnvtdw + m_r[m_ssw.bits.u][op.macro & 0xf] = (int32_t)m_f[(op.macro >> 4) & 0xf]; + break; + + case 0x37: // cnvwd + m_f[op.macro & 0xf] = (double)m_r[m_ssw.bits.u][(op.macro >> 4) & 0xf]; + break; + + default: + logerror("illegal macro opcode at 0x%08x\n", m_pc); + break; + } + + break; + + case 0xB6: + if (m_ssw.bits.u == 0) + { + switch (insn & 0xFF) + { + case 0x00: // movus + m_r[0][op.macro & 0xf] = m_r[1][(op.macro >> 4) & 0xf]; + // setcc1(m_r[m_ssw.bits.u][r2]); + break; + + case 0x01: // movsu + m_r[1][op.macro & 0xf] = m_r[0][(op.macro >> 4) & 0xf]; + // setcc1(m_r[!m_ssw.bits.u][r2]); + break; + + case 0x04: + // reti: restore psw, ssw and pc from supervisor stack +#if SSP_HACK + // some user code increments sp before reti + // HACK: if previous instruction is addq $12,sp then correct sp + // FIXME: figure out the real cause of this stack adjustment + if (m_program->read_word(m_pc - 2) == 0x82cf) + { + LOG_INTERRUPT("correcting ssp for reti at 0x%08x\n", m_pc); + + m_r[0][(op.macro >> 4) & 0xf] -= 12; + } +#endif + LOG_INTERRUPT("reti r%d, ssp = %08x, pc = %08x, next_pc = %08x\n", + (op.macro >> 4) & 0xf, m_r[0][(op.macro >> 4) & 0xf], m_pc, m_program->read_dword(m_r[0][(op.macro >> 4) & 0xf] + 8)); + m_psw.d = m_program->read_dword(m_r[0][(op.macro >> 4) & 0xf] + 0); + m_ssw.d = m_program->read_dword(m_r[0][(op.macro >> 4) & 0xf] + 4); + next_pc = m_program->read_dword(m_r[0][(op.macro >> 4) & 0xf] + 8); + + m_r[0][(op.macro >> 4) & 0xf] += 12; + break; + + default: + // TODO: set psw.b.cts + logerror("illegal opcode at 0x%08x\n", m_pc); + next_pc = intrap(0x300, next_pc); // illegal operation, illegal operation + machine().debug_break(); + break; + } + } + else + // TODO: set psw.b.cts + next_pc = intrap(0x308, next_pc); // illegal operation, privileged instruction + break; + + default: + logerror("illegal opcode at 0x%08x\n", m_pc); + next_pc = intrap(0x300, next_pc); // illegal operation, illegal operation + machine().debug_break(); + break; + + } + + return next_pc; +} + +/* +* Sets up the PC, SSW and PSW to handle an exception. +*/ +uint32_t clipper_device::intrap(uint32_t vector, uint32_t pc) +{ + uint32_t next_pc, pmode; + + LOG_INTERRUPT("intrap - vector %x, pc = 0x%08x, next_pc = 0x%08x, ssp = 0x%08x\n", vector, pc, m_program->read_dword(vector + 4), m_r[0][15]); + + // push pc, psw and ssw onto supervisor stack + m_program->write_dword(m_r[0][15] - 4, pc); + m_program->write_dword(m_r[0][15] - 12, m_psw.d); // TODO: set mts or cts bits in saved psw + m_program->write_dword(m_r[0][15] - 8, m_ssw.d); + + // decrement supervisor stack pointer +#if SSP_HACK + m_r[0][15] -= 12; +#else + m_r[0][15] -= 24; +#endif + + // load SSW from trap vector and set previous mode flag + pmode = m_ssw.bits.u; + m_ssw.d = m_program->read_dword(vector + 0); + m_ssw.bits.p = pmode; + + // clear psw + m_psw.d = 0; + + // fetch target pc + next_pc = m_program->read_dword(vector + 4); + +#if SSP_HACK + // some code immediately increments sp by 12 on entry into isr (causing overwrite of exception frame) + // HACK: if target instruction is addq $12,sp then adjust sp + // FIXME: figure out the real cause of this stack adjustment + if (m_program->read_word(next_pc) == 0x82cf) + { + LOG_INTERRUPT("correcting ssp for trap handler at 0x%08x\n", next_pc); + + m_r[0][15] -= 12; + } +#endif + + // return new pc from trap vector + return next_pc; +} + +void clipper_device::evaluate_cc1(int32_t v0) +{ + m_psw.bits.n = v0 < 0; + m_psw.bits.z = v0 == 0; +} + +/* +evaluation of overflow: +both +: v0 - v1 > v0 +v0- v1+: v0 - v1 > v0 = 0 + +both -: v0 - v1 < v0 +v0+ v1-: v0 - v1 < v0 = 0 + +overflow (addition): inputs have same sign and output has opposite +s1 == s2 && != s3 +*/ +void clipper_device::evaluate_cc2(int32_t v0, int32_t v1) +{ + m_psw.bits.n = (v0 - v1) >> 31; + m_psw.bits.z = v0 == v1; + m_psw.bits.v = ((v1 < 0) ? (v0 - v1 < v0) : (v0 - v1 > v0)); + m_psw.bits.c = ((uint32_t)v0 < (uint32_t)v1); +} + +void clipper_device::evaluate_cc3(int32_t v0, int32_t v1, int32_t v2) +{ + m_psw.bits.n = v2 >> 31; + m_psw.bits.z = v2 == 0; + m_psw.bits.v = ((v0 > 0 && v1 > 0 && v2 < 0) || (v0 < 0 && v1 < 0 && v2 > 0)); + m_psw.bits.v = ((v1 < 0) ? (v0 - v1 < v0) : (v0 - v1 > v0)); + m_psw.bits.c = ((uint32_t)v0 < (uint32_t)v1); +} + +void clipper_device::evaluate_cc2f(double v0, double v1) +{ + m_psw.bits.n = v0 < v1; + m_psw.bits.z = v0 == v1; + // psw.b.v = ((v1 < 0) ? (v0 - v1 < v0) : (v0 - v1 > v0)); + // psw.b.c = (fabs(v0) < fabs(v1)); + m_psw.bits.v = 0; + m_psw.bits.c = 0; +} + +bool clipper_device::evaluate_branch(uint32_t r2) +{ + switch (r2) + { + case BRANCH_T: + return true; + + case BRANCH_GT: + return !((m_psw.bits.n ^ m_psw.bits.v) | m_psw.bits.z); + case BRANCH_GE: + return !(m_psw.bits.n ^ m_psw.bits.v); + case BRANCH_EQ: + return m_psw.bits.z; + + case BRANCH_LT: + return m_psw.bits.n ^ m_psw.bits.v; + case BRANCH_LE: + return (m_psw.bits.n ^ m_psw.bits.v) | m_psw.bits.z; + case BRANCH_NE: + return !m_psw.bits.z; + + case BRANCH_GTU: + return !(m_psw.bits.c | m_psw.bits.z); + case BRANCH_GEU: + return !m_psw.bits.c; + case BRANCH_LTU: + return m_psw.bits.c; + case BRANCH_LEU: + return m_psw.bits.c | m_psw.bits.z; + + case BRANCH_V: + return m_psw.bits.v; + case BRANCH_NV: + return !m_psw.bits.v; + case BRANCH_N: + return m_psw.bits.n; + case BRANCH_NN: + return !m_psw.bits.n; + + case BRANCH_FV: + return false; + } + + return false; +} + +offs_t clipper_device::disasm_disassemble(std::ostream &stream, offs_t pc, const uint8_t *oprom, const uint8_t *opram, uint32_t options) +{ + extern CPU_DISASSEMBLE(clipper); + return CPU_DISASSEMBLE_NAME(clipper)(this, stream, pc, oprom, opram, options); +} diff --git a/src/devices/cpu/clipper/clipper.h b/src/devices/cpu/clipper/clipper.h new file mode 100644 index 00000000000..ad8c28421a1 --- /dev/null +++ b/src/devices/cpu/clipper/clipper.h @@ -0,0 +1,165 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay +#pragma once + +#ifndef __CLIPPER_H__ +#define __CLIPPER_H__ + +// enumerate registers (why?) +enum +{ + CLIPPER_R0, CLIPPER_R1, CLIPPER_R2, CLIPPER_R3, CLIPPER_R4, CLIPPER_R5, CLIPPER_R6, CLIPPER_R7, + CLIPPER_R8, CLIPPER_R9, CLIPPER_R10, CLIPPER_R11, CLIPPER_R12, CLIPPER_R13, CLIPPER_R14, CLIPPER_R15, + + CLIPPER_F0, CLIPPER_F1, CLIPPER_F2, CLIPPER_F3, CLIPPER_F4, CLIPPER_F5, CLIPPER_F6, CLIPPER_F7, + CLIPPER_F8, CLIPPER_F9, CLIPPER_F10, CLIPPER_F11, CLIPPER_F12, CLIPPER_F13, CLIPPER_F14, CLIPPER_F15, + + CLIPPER_PSW, CLIPPER_SSW, + CLIPPER_PC +}; + +enum +{ + ADDR_MODE_NONE = 0x00, + ADDR_MODE_PC32 = 0x10, + ADDR_MODE_ABS32 = 0x30, + ADDR_MODE_REL32 = 0x60, + ADDR_MODE_PC16 = 0x90, + ADDR_MODE_REL12 = 0xA0, + ADDR_MODE_ABS16 = 0xB0, + ADDR_MODE_PCX = 0xD0, + ADDR_MODE_RELX = 0xE0, + ADDR_MODE_REL = 0xFF +}; + +// branch conditions +enum +{ + BRANCH_T = 0x0, + BRANCH_GT = 0x1, + BRANCH_GE = 0x2, + BRANCH_EQ = 0x3, + BRANCH_LT = 0x4, + BRANCH_LE = 0x5, + BRANCH_NE = 0x6, + BRANCH_GTU = 0x7, + BRANCH_GEU = 0x8, + BRANCH_LTU = 0x9, + BRANCH_LEU = 0xA, + BRANCH_V = 0xB, + BRANCH_NV = 0xC, + BRANCH_N = 0xD, + BRANCH_NN = 0xE, + BRANCH_FV = 0xF +}; + +class clipper_device : public cpu_device +{ +public: + clipper_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + + // device_execute_interface overrides + virtual uint32_t execute_min_cycles() const override { return 1; }; + virtual uint32_t execute_max_cycles() const override { return 1; }; // FIXME: don't know, especially macro instructions + virtual uint32_t execute_input_lines() const override { return 16; }; // FIXME: number of input/interrupt lines + virtual void execute_run() override; + virtual void execute_set_input(int inputnum, int state) override; + + // device_memory_interface overrides + virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const override; + + // device_state_interface overrides +#if 0 + virtual void state_import(const device_state_entry &entry) override; + virtual void state_export(const device_state_entry &entry) override; +#endif + virtual void state_string_export(const device_state_entry &entry, std::string &str) const override; + + // device_disasm_interface overrides + virtual uint32_t disasm_min_opcode_bytes() const override { return 2; } // smallest instruction + virtual uint32_t disasm_max_opcode_bytes() const override { return 8; } // largest instruction + virtual offs_t disasm_disassemble(std::ostream &stream, offs_t pc, const uint8_t *oprom, const uint8_t *opram, uint32_t options) override; + + // core registers + uint32_t m_pc; + union { + struct { + uint32_t n : 1; // negative + uint32_t z : 1; // zero + uint32_t v : 1; // overflow + uint32_t c : 1; // carry out or borrow in + uint32_t fx : 1; // floating inexact + uint32_t fu : 1; // floating underflow + uint32_t fd : 1; // floating divide by zero + uint32_t fv : 1; // floating overflow + uint32_t fi : 1; // floating invalid operation + uint32_t efx : 1; // enable floating inexact trap + uint32_t efu : 1; // enable floating underflow trap + uint32_t efd : 1; // enable floating divide by zero trap + uint32_t efv : 1; // enable floating overflow trap + uint32_t efi : 1; // enable floating invalid operation trap + uint32_t eft : 1; // enable floating trap + uint32_t fr : 2; // floating rounding mode + uint32_t : 3; + uint32_t dsp : 2; // c400 - delay slot pointer + uint32_t big : 1; // c400 - big endian (hardware) + uint32_t t : 1; // trace trap + uint32_t cts : 4; // cpu trap status + uint32_t mts : 4; // memory trap status + } bits; + uint32_t d; + } m_psw; + union { + struct { + uint32_t in : 4; // interrupt number + uint32_t il : 4; // interrupt level + uint32_t ei : 1; // enable interrupts + uint32_t id : 8; // cpu rev # and type + uint32_t : 5; + uint32_t frd : 1; // floating registers dirty + uint32_t tp : 1; // trace trap pending + uint32_t ecm : 1; // enable corrected memory error + uint32_t df : 1; // fpu disabled + uint32_t m : 1; // mapped mode + uint32_t ku : 1; // user protect key + uint32_t uu : 1; // user data mode + uint32_t k : 1; // protect key + uint32_t u : 1; // user mode + uint32_t p : 1; // previous mode + } bits; + uint32_t d; + } m_ssw; + int32_t m_r[2][16]; + double m_f[16]; + +private: + address_space_config m_program_config; + + address_space *m_program; + direct_read_data *m_direct; + + int m_icount; + int m_interrupt_cycles; + + int m_immediate_irq; + int m_immediate_vector; + + int clipper_device::execute_instruction(uint16_t insn); + + // condition code evaluations + void clipper_device::evaluate_cc1(int32_t v0); + void clipper_device::evaluate_cc2(int32_t v0, int32_t v1); + void clipper_device::evaluate_cc3(int32_t v0, int32_t v1, int32_t v2); + void clipper_device::evaluate_cc2f(double v0, double v1); + bool clipper_device::evaluate_branch(uint32_t r2); + + uint32_t clipper_device::intrap(uint32_t vector, uint32_t pc); +}; + +extern const device_type CLIPPER; +#endif /* __CLIPPER_H__ */ diff --git a/src/devices/cpu/clipper/clipperd.cpp b/src/devices/cpu/clipper/clipperd.cpp new file mode 100644 index 00000000000..414817e3a8f --- /dev/null +++ b/src/devices/cpu/clipper/clipperd.cpp @@ -0,0 +1,239 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#include "emu.h" + +enum +{ + ADDR_MODE_PC32 = 0x10, + ADDR_MODE_ABS32 = 0x30, + ADDR_MODE_REL32 = 0x60, + ADDR_MODE_PC16 = 0x90, + ADDR_MODE_REL12 = 0xA0, + ADDR_MODE_ABS16 = 0xB0, + ADDR_MODE_PCX = 0xD0, + ADDR_MODE_RELX = 0xE0, +}; + +#define R1 ((insn[0] & 0x00F0) >> 4) +#define R2 (insn[0] & 0x000F) + +#define I16 ((int16_t)insn[1]) +#define I32 (*(int32_t *)&insn[1]) +#define IMM_VALUE (insn[0] & 0x0080 ? I16 : I32) +#define IMM_SIZE (insn[0] & 0x0080 ? 2 : 4) + +#define ADDR_MODE (insn[0] & 0x00F0) +#define ADDR_R2 ((insn[0] & 0x0050) == 0x0010 ? (insn[0] & 0x000F) : (insn[1] & 0x000F)) +#define ADDR_SIZE (ADDR_MODE > ADDR_MODE_REL32 ? 2 : ADDR_MODE == ADDR_MODE_REL32 ? 6 : 4) +#define ADDR_RX ((insn[1] & 0xF0) >> 4) +#define ADDR_I12 (((int16_t)insn[1]) >> 4) + +char *address (offs_t pc, uint16_t *insn) +{ + static char buffer[32]; + + switch (ADDR_MODE) + { + case ADDR_MODE_PC32: sprintf(buffer, "0x%X", pc + I32); break; + case ADDR_MODE_ABS32: sprintf(buffer, "0x%X", I32); break; + case ADDR_MODE_REL32: sprintf(buffer, "%d(r%d)", *(int32_t *)&insn[2], R2); break; + case ADDR_MODE_PC16: sprintf(buffer, "0x%X", pc + I16); break; + case ADDR_MODE_REL12: sprintf(buffer, "%d(r%d)", ADDR_I12, R2); break; + case ADDR_MODE_ABS16: sprintf(buffer, "0x%X", I16); break; + case ADDR_MODE_PCX: sprintf(buffer, "[r%d](pc)", ADDR_RX); break; + case ADDR_MODE_RELX: sprintf(buffer, "[r%d](r%d)", ADDR_RX, R2); break; + default: sprintf(buffer, "ERROR"); break; + } + + return buffer; +} + +CPU_DISASSEMBLE(clipper) +{ + uint16_t *insn = (uint16_t *)oprom; + offs_t bytes; + + // TODO: substitute for 'fp' and 'sp' register names? + // TODO: branch conditions + + switch (insn[0] >> 8) + { + case 0x00: + if (oprom[0] == 0) + util::stream_format(stream, "noop"); + else + util::stream_format(stream, "noop $%d", oprom[0]); + bytes = 2; + break; + + case 0x10: util::stream_format(stream, "movwp r%d,%s", R2, R1 == 0 ? "psw" : R1 == 1 ? "ssw" : "sswf"); bytes = 2; break; + case 0x11: util::stream_format(stream, "movpw %s,r%d", R1 == 0 ? "psw" : "ssw", R2); bytes = 2; break; + case 0x12: util::stream_format(stream, "calls $%d", insn[0] & 0x7F); bytes = 2; break; + case 0x13: util::stream_format(stream, "ret r%d", R2); bytes = 2; break; + case 0x14: util::stream_format(stream, "pushw r%d,r%d", R2, R1); bytes = 2; break; + + case 0x16: util::stream_format(stream, "popw r%d,r%d", R1, R2); bytes = 2; break; + + case 0x20: util::stream_format(stream, "adds f%d,f%d", R1, R2); bytes = 2; break; + case 0x21: util::stream_format(stream, "subs f%d,f%d", R1, R2); bytes = 2; break; + case 0x22: util::stream_format(stream, "addd f%d,f%d", R1, R2); bytes = 2; break; + case 0x23: util::stream_format(stream, "subd f%d,f%d", R1, R2); bytes = 2; break; + case 0x24: util::stream_format(stream, "movs f%d,f%d", R1, R2); bytes = 2; break; + case 0x25: util::stream_format(stream, "cmps f%d,f%d", R1, R2); bytes = 2; break; + case 0x26: util::stream_format(stream, "movd f%d,f%d", R1, R2); bytes = 2; break; + case 0x27: util::stream_format(stream, "cmpd f%d,f%d", R1, R2); bytes = 2; break; + case 0x28: util::stream_format(stream, "muls f%d,f%d", R1, R2); bytes = 2; break; + case 0x29: util::stream_format(stream, "divs f%d,f%d", R1, R2); bytes = 2; break; + case 0x2A: util::stream_format(stream, "muld f%d,f%d", R1, R2); bytes = 2; break; + case 0x2B: util::stream_format(stream, "divd f%d,f%d", R1, R2); bytes = 2; break; + case 0x2C: util::stream_format(stream, "movsw f%d,r%d", R1, R2); bytes = 2; break; + case 0x2D: util::stream_format(stream, "movws r%d,f%d", R1, R2); bytes = 2; break; + case 0x2E: util::stream_format(stream, "movdl f%d,r%d:%d", R1, R2 + 0, R2 + 1); bytes = 2; break; + case 0x2F: util::stream_format(stream, "movld r%d:r%d,f%d", R1 + 0, R1 + 1, R2); bytes = 2; break; + + case 0x30: util::stream_format(stream, "shaw r%d,r%d", R1, R2); bytes = 2; break; + case 0x31: util::stream_format(stream, "shal r%d,r%d:r%d", R1, R2 + 0, R2 + 1); bytes = 2; break; + case 0x32: util::stream_format(stream, "shlw r%d,r%d", R1, R2); bytes = 2; break; + case 0x33: util::stream_format(stream, "shll r%d,r%d:r%d", R1, R2 + 0, R2 + 1); bytes = 2; break; + case 0x34: util::stream_format(stream, "rotw r%d,r%d", R1, R2); bytes = 2; break; + case 0x35: util::stream_format(stream, "rotl r%d,r%d:r%d", R1, R2 + 0, R2 + 1); bytes = 2; break; + + case 0x38: util::stream_format(stream, "shai $%d,r%d", I16, R2); bytes = 4; break; + case 0x39: util::stream_format(stream, "shali $%d,r%d:r%d", I16, R2 + 0, R2 + 1); bytes = 4; break; + case 0x3A: util::stream_format(stream, "shli $%d,r%d", I16, R2); bytes = 4; break; + case 0x3B: util::stream_format(stream, "shlli $%d,r%d:r%d", I16, R2 + 0, R2 + 1); bytes = 4; break; + case 0x3C: util::stream_format(stream, "roti $%d,r%d", I16, R2); bytes = 4; break; + case 0x3D: util::stream_format(stream, "rotli $%d,r%d:r%d", I16, R2 + 0, R2 + 1); bytes = 4; break; + + case 0x44: util::stream_format(stream, "call r%d,(r%d)", R2, R1); bytes = 2; break; + case 0x45: util::stream_format(stream, "call r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break; + + case 0x48: util::stream_format(stream, "b* (r%d)", R1); bytes = 2; break; + case 0x49: util::stream_format(stream, "b* %s", address(pc, insn)); bytes = 2 + ADDR_SIZE; break; + + case 0x4C: util::stream_format(stream, "bf* (r%d)", R1); bytes = 2; break; + case 0x4D: util::stream_format(stream, "bf* %s", address(pc, insn)); bytes = 2 + ADDR_SIZE; break; + + case 0x60: util::stream_format(stream, "loadw (r%d),r%d", R1, R2); bytes = 2; break; + case 0x61: util::stream_format(stream, "loadw %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break; + case 0x62: util::stream_format(stream, "loada (r%d),r%d", R1, R2); bytes = 2; break; + case 0x63: util::stream_format(stream, "loada %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break; + case 0x64: util::stream_format(stream, "loads (r%d),f%d", R1, R2); bytes = 2; break; + case 0x65: util::stream_format(stream, "loads %s,f%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break; + case 0x66: util::stream_format(stream, "loadd (r%d),f%d", R1, R2); bytes = 2; break; + case 0x67: util::stream_format(stream, "loadd %s,f%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break; + case 0x68: util::stream_format(stream, "loadb (r%d),r%d", R1, R2); bytes = 2; break; + case 0x69: util::stream_format(stream, "loadb %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break; + case 0x6A: util::stream_format(stream, "loadbu (r%d),r%d", R1, R2); bytes = 2; break; + case 0x6B: util::stream_format(stream, "loadbu %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break; + case 0x6C: util::stream_format(stream, "loadh (r%d),r%d", R1, R2); bytes = 2; break; + case 0x6D: util::stream_format(stream, "loadh %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break; + case 0x6E: util::stream_format(stream, "loadhu (r%d),r%d", R1, R2); bytes = 2; break; + case 0x6F: util::stream_format(stream, "loadhu %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break; + + case 0x70: util::stream_format(stream, "storw r%d,(r%d)", R2, R1); bytes = 2; break; + case 0x71: util::stream_format(stream, "storw r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break; + case 0x72: util::stream_format(stream, "tsts (r%d),r%d", R1, R2); bytes = 2; break; + case 0x73: // tsts + case 0x74: util::stream_format(stream, "stors f%d,(r%d)", R2, R1); bytes = 2; break; + case 0x75: util::stream_format(stream, "stors f%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break; + case 0x76: util::stream_format(stream, "stord f%d,(r%d)", R2, R1); bytes = 2; break; + case 0x77: util::stream_format(stream, "stord f%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break; + case 0x78: util::stream_format(stream, "storb r%d,(r%d)", R2, R1); bytes = 2; break; + case 0x79: util::stream_format(stream, "storb r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break; + + case 0x7C: util::stream_format(stream, "storh r%d,(r%d)", R2, R1); bytes = 2; break; + case 0x7D: util::stream_format(stream, "storh r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break; + + case 0x80: util::stream_format(stream, "addw r%d,r%d", R1, R2); bytes = 2; break; + + case 0x82: util::stream_format(stream, "addq $%d,r%d", R1, R2); bytes = 2; break; + case 0x83: util::stream_format(stream, "addi $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break; + case 0x84: util::stream_format(stream, "movw r%d,r%d", R1, R2); bytes = 2; break; + + case 0x86: util::stream_format(stream, "loadq $%d,r%d", R1, R2); bytes = 2; break; + case 0x87: util::stream_format(stream, "loadi $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break; + case 0x88: util::stream_format(stream, "andw r%d,r%d", R1, R2); bytes = 2; break; + + case 0x8B: util::stream_format(stream, "andi $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break; + case 0x8C: util::stream_format(stream, "orw r%d,r%d", R1, R2); bytes = 2; break; + + case 0x8F: util::stream_format(stream, "ori $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break; + + case 0x90: util::stream_format(stream, "addwc r%d,r%d", R1, R2); bytes = 2; break; + case 0x91: util::stream_format(stream, "subwc r%d,r%d", R1, R2); bytes = 2; break; + + case 0x93: util::stream_format(stream, "negw r%d,r%d", R1, R2); bytes = 2; break; + + case 0x98: util::stream_format(stream, "mulw r%d,r%d", R1, R2); bytes = 2; break; + case 0x99: util::stream_format(stream, "mulwx r%d,r%d:r%d", R1, R2 + 0, R2 + 1); bytes = 2; break; + case 0x9A: util::stream_format(stream, "mulwu r%d,r%d", R1, R2); bytes = 2; break; + case 0x9B: util::stream_format(stream, "mulwux r%d,r%d:r%d", R1, R2 + 0, R2 + 1); bytes = 2; break; + case 0x9C: util::stream_format(stream, "divw r%d,r%d", R1, R2); bytes = 2; break; + case 0x9D: util::stream_format(stream, "modw r%d,r%d", R1, R2); bytes = 2; break; + case 0x9E: util::stream_format(stream, "divwu r%d,r%d", R1, R2); bytes = 2; break; + case 0x9F: util::stream_format(stream, "modwu r%d,r%d", R1, R2); bytes = 2; break; + + case 0xA0: util::stream_format(stream, "subw r%d,r%d", R1, R2); bytes = 2; break; + + case 0xA2: util::stream_format(stream, "subq $%d,r%d", R1, R2); bytes = 2; break; + case 0xA3: util::stream_format(stream, "subi $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break; + case 0xA4: util::stream_format(stream, "cmpw r%d,r%d", R1, R2); bytes = 2; break; + + case 0xA6: util::stream_format(stream, "cmpq $%d,r%d", R1, R2); bytes = 2; break; + case 0xA7: util::stream_format(stream, "cmpi $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break; + case 0xA8: util::stream_format(stream, "xorw r%d,r%d", R1, R2); bytes = 2; break; + + case 0xAB: util::stream_format(stream, "xori $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break; + case 0xAC: util::stream_format(stream, "notw r%d,r%d", R1, R2); bytes = 2; break; + + case 0xAE: util::stream_format(stream, "notq $%d,r%d", R1, R2); bytes = 2; break; + + case 0xB4: // macro + case 0xB5: // macro + switch (insn[0] & 0xff) + { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: + util::stream_format(stream, "savew%d", R2); + break; + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: + case 0x1C: + util::stream_format(stream, "restw%d", R2); + break; + + default: + util::stream_format(stream, "macro 0x%04X %04X", insn[0], insn[1]); + break; + } + bytes = 4; + break; + case 0xB6: // macro + case 0xB7: // macro + switch (insn[0] & 0xff) + { + case 0x00: util::stream_format(stream, "movus r%d,r%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break; + case 0x01: util::stream_format(stream, "movsu r%d,r%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break; + case 0x04: util::stream_format(stream, "reti r%d", (insn[1] & 0xf0) >> 4); break; + + default: + util::stream_format(stream, "macro 0x%04X %04X", insn[0], insn[1]); + break; + } + bytes = 4; + break; + + default: + util::stream_format(stream, ".word 0x%04X ; invalid", insn[0]); + bytes = 2; + break; + } + + return bytes; +} \ No newline at end of file diff --git a/src/mame/drivers/interpro.cpp b/src/mame/drivers/interpro.cpp new file mode 100644 index 00000000000..167b0354790 --- /dev/null +++ b/src/mame/drivers/interpro.cpp @@ -0,0 +1,462 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#include "includes/interpro.h" +#include "debugger.h" + +#define VERBOSE 0 +#if VERBOSE +#define LOG_EMERALD(...) logerror(__VA_ARGS__) +#define LOG_MCGA(...) logerror(__VA_ARGS__) +#define LOG_IDPROM(...) logerror(__VA_ARGS__) +#else +#define LOG_EMERALD(...) {} +#define LOG_MCGA(...) {} +#define LOG_IDPROM(...) logerror(__VA_ARGS__) +#endif + +// machine start +void interpro_state::machine_start() +{ +} + +/* +* MCGA Control Register definitions. +*/ +#define MCGA_CTRL_OPTMASK 0x00000003 +#define MCGA_CTRL_CBITFRCRD 0x00000004 +#define MCGA_CTRL_CBITFRCSUB 0x00000008 +#define MCGA_CTRL_ENREFRESH 0x00000010 +#define MCGA_CTRL_ENMSBE 0x00000100 +#define MCGA_CTRL_ENMMBE 0x00000200 // multi-master bus enable? +#define MCGA_CTRL_ENECC 0x00000400 +#define MCGA_CTRL_WRPROT 0x00008000 +/* +* MCGA Error Register definitions. +*/ +#define MCGA_ERROR_SYNDMASK 0x000000ff +#define MCGA_ERROR_SYNDSHIFT 0 +#define MCGA_ERROR_SYND(X) (((X) & MCGA_ERROR_SYNDMASK) >> \ + MCGA_ERROR_SYNDSHIFT) +#define MCGA_ERROR_MMBE 0x00000100 +#define MCGA_ERROR_MSBE 0x00000200 +#define MCGA_ERROR_ADDRMASK 0x00001C00 +#define MCGA_ERROR_ADDRSHIFT 7 +#define MCGA_ERROR_ADDR(X) (((X) & MCGA_ERROR_ADDRMASK) >> \ + MCGA_ERROR_ADDRSHIFT) +#define MCGA_ERROR_VALID 0x00008000 + +/* +* MCGA Control Register definitions. +*/ +#define MCGA_MEMSIZE_ADDRMASK 0x0000007F +#define MCGA_MEMSIZE_ADDRSHIFT 24 +#define MCGA_MEMSIZE_ADDR(X) (((X) & MCGA_MEMSIZE_ADDRMASK) << \ + MCGA_MEMSIZE_ADDRSHIFT) + +#define E_SREG_LED 0 +#define E_SREG_ERROR 0 +#define E_SREG_STATUS 1 +#define E_SREG_CTRL1 2 +#define E_SREG_CTRL2 3 + +/* +* Error Register Bit Definitions +* WARNING: Some definitions apply only to certain hardware +* (ie: E_SERR_SRX* is only valid on 6600 class machines) +*/ +#define E_SERR_BPID4 0x0001 +#define E_SERR_SRXMMBE 0x0002 +#define E_SERR_SRXHOG 0x0004 +#define E_SERR_SRXNEM 0x0008 +#define E_SERR_SRXVALID 0x0010 +#define E_SERR_CBUSNMI 0x0020 +#define E_SERR_CBGMASK 0x00c0 +#define E_SERR_CBGSHIFT 6 +#define E_SERR_BG_MASK 0x0070 +#define E_SERR_BG_SHIFT 4 +#define E_SERR_BUSHOG 0x0080 +#define E_SERR_BG(X) (((X) & E_SERR_BG_MASK) >> E_SERR_BG_SHIFT) +#define CBUS_ID(X) (((X) & E_SERR_CBGMASK) >> E_SERR_CBGSHIFT) + +/* +* Status Register Bit Definitions +*/ +#define E_STAT_YELLOW_ZONE 0x0001 +#define E_STAT_SRNMI 0x0002 +#define E_STAT_PWRLOSS 0x0004 +#define E_STAT_RED_ZONE 0x0008 +#define E_STAT_BP_MASK 0x00f0 +#define E_STAT_BP_SHIFT 4 +#define E_STAT_BP(X) (((X) & E_STAT_BP_MASK) >> E_STAT_BP_SHIFT) + +/* +* Control/Status Register 1 Bit Definitions +*/ +#define E_CTRL1_FLOPLOW 0x0001 +#define E_CTRL1_FLOPRDY 0x0002 +#define E_CTRL1_LEDENA 0x0004 +#define E_CTRL1_LEDDP 0x0008 +#define E_CTRL1_ETHLOOP 0x0010 +#define E_CTRL1_ETHDTR 0x0020 +#define E_CTRL1_ETHRMOD 0x0040 +#define E_CTRL1_CLIPRESET 0x0040 +#define E_CTRL1_FIFOACTIVE 0x0080 + +/* +* Control/Status Register 2 Bit Definitions +*/ +#define E_CTRL2_PWRUP 0x0001 +#define E_CTRL2_PWRENA 0x0002 +#define E_CTRL2_HOLDOFF 0x0004 +#define E_CTRL2_EXTNMIENA 0x0008 +#define E_CTRL2_COLDSTART 0x0010 +#define E_CTRL2_RESET 0x0020 +#define E_CTRL2_BUSENA 0x0040 +#define E_CTRL2_FRCPARITY 0x0080 +#define E_CTRL2_FLASHEN 0x0080 +#define E_CTRL2_WMASK 0x000f +#define E_SET_CTRL2(X) E_SREG_CTRL2 = (E_SREG_CTRL2 & \ + E_CTRL2_WMASK) | (X) +#define E_CLR_CTRL2(X) E_SREG_CTRL2 &= E_CTRL2_WMASK & ~(X) + +void interpro_state::machine_reset() +{ + // flash rom requires the following values + m_emerald_reg[E_SREG_ERROR] = 0x00; + m_emerald_reg[E_SREG_STATUS] = 0x00; + m_emerald_reg[E_SREG_CTRL1] = E_CTRL1_FLOPRDY; + m_emerald_reg[E_SREG_CTRL2] = E_CTRL2_COLDSTART; + + m_mcga[0] = 0x00ff; // 0x00 + m_mcga[2] = MCGA_CTRL_ENREFRESH | MCGA_CTRL_CBITFRCSUB | MCGA_CTRL_CBITFRCRD; // 0x08 ctrl + //m_mcga[4] = 0x8000; // 0x10 error + m_mcga[10] = 0x00ff; // 0x28 + m_mcga[14] = 0x0340; // 0x38 memsize +} + +WRITE8_MEMBER(interpro_state::emerald_w) +{ + switch (offset) + { + case E_SREG_LED: + LOG_EMERALD("led: value %d at pc 0x%08x\n", data, space.device().safe_pc()); + m_led->a_w(data); + break; + + case E_SREG_STATUS: // not sure if writable? + case E_SREG_CTRL1: + LOG_EMERALD("emerald write offset %d data 0x%x pc 0x%08x\n", offset, data, space.device().safe_pc()); + + if ((data ^ m_emerald_reg[offset]) & E_CTRL1_LEDDP) + LOG_EMERALD("emerald led decimal point %s\n", data & E_CTRL1_LEDDP ? "on" : "off"); + + m_emerald_reg[offset] = data; + break; + + case E_SREG_CTRL2: + LOG_EMERALD("emerald write offset %d data 0x%x pc 0x%08x\n", offset, data, space.device().safe_pc()); + if (data & 0x20) + // reset + m_maincpu->reset(); + else + m_emerald_reg[offset] = data & 0x0f; // top four bits are not persistent + break; + } +} + +READ8_MEMBER(interpro_state::emerald_r) +{ + LOG_EMERALD("emerald read offset %d pc 0x%08x\n", offset, space.device().safe_pc()); + switch (offset) + { + case E_SREG_ERROR: + case E_SREG_STATUS: + case E_SREG_CTRL1: + case E_SREG_CTRL2: + default: + return m_emerald_reg[offset]; + break; + } +} + +WRITE16_MEMBER(interpro_state::mcga_w) +{ + /* + read MEMSIZE 0x38 mask 0xffff + read 0x00 mask 0x0000 + write CBSUB 0x20 mask 0x00ff data 0 + write FRCRD 0x18 mask 0x00ff data 0 + read ERROR 0x10 mask 0xffff + read 0x00 mask 0xffff + + (0x38 >> 8) & 0xF == 3? + + if (0x00 != 0xFF) -> register reset error + + 0x00 = 0x0055 (test value & 0xff) + r7 = 0x00 & 0xff + */ + LOG_MCGA("mcga write offset = 0x%08x, mask = 0x%08x, data = 0x%08x, pc = 0x%08x\n", offset, mem_mask, data, space.device().safe_pc()); + switch (offset) + { + case 0x02: // MCGA_CTRL + // HACK: set or clear error status depending on ENMMBE bit + if (data & MCGA_CTRL_ENMMBE) + m_mcga[4] |= MCGA_ERROR_VALID; +// else +// m_mcga[4] &= ~MCGA_ERROR_VALID; + + default: + m_mcga[offset] = data; + break; + } +} + +READ16_MEMBER(interpro_state::mcga_r) +{ + LOG_MCGA("mcga read offset = 0x%08x, mask = 0x%08x, pc = 0x%08x\n", offset, mem_mask, space.device().safe_pc()); + + switch (offset) + { + default: + return m_mcga[offset]; + } +} + +/* +* ID Prom Structure +*/ +struct IDprom { + char i_board[8]; /* string of board type */ + char i_eco[8]; /* ECO flags */ + char i_feature[8]; /* feature flags */ + char i_reserved[2]; /* reserved (all 1's) */ + short i_family; /* family code */ + char i_footprint[4]; /* footprint/checksum */ +}; + + +READ32_MEMBER(interpro_state::idprom_r) +{ + LOG_IDPROM("idprom read offset 0x%x mask 0x%08x at 0x%08x\n", offset, mem_mask, space.device().safe_pc()); + + uint32_t speed = 70000000; + + static uint8_t idprom[] = { +#if 0 + // module type id + 'M', 'P', 'C', 'B', + '*', '*', '*', '*', + + // ECO bytes + 0x87, 0x65, 0x43, 0x21, + 0xbb, 0xcc, 0xdd, 0xee, + + // the following 8 bytes are "feature bytes" + // the feature bytes contain a 32 bit word which is divided by 40000 + // if they're empty, a default value of 50 000 000 is used + // perhaps this is a system speed (50MHz)? + 0x12, 0x34, 0x56, 0x78, + (speed >> 24) & 0xff, (speed >> 16) & 0xff, (speed >> 8) & 0xff, (speed >> 0) & 0xff, + + // reserved bytes + 0xff, 0xff, + + // family + // boot rom tests for family == 0x41 or 0x42 + // if so, speed read from feature bytes 2 & 3 + // if not, read speed from feature bytes 4-7 + 0x00, 0x40, +#else + // Carl Friend's 2020 + 0x00, 0x00, 0x00, 0x00, '9', '6', '2', 'A', // board + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // eco + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // feature + 0xff, 0xff, // reserved + 0x24, 0x00, // family +#endif + // footprint and checksum + 0x55, 0xaa, 0x55, 0x00 + }; + + switch (offset) + { + case 0x1f: + { + uint8_t sum = 0; + + // compute the checksum (sum of all bytes must be == 0x00) + for (int i = 0; i < 0x20; i++) + sum += idprom[i]; + + return 0x100 - (sum & 0xff); + } + + default: + return idprom[offset]; + } +} + +READ32_MEMBER(interpro_state::slot0_r) +{ + //static struct IDprom slot0 = { "bordtyp", "ecoflgs", "featurs", { '\xff', '\xff' }, 0x7ead, "chk" }; + + // Carl Friend's Turqoise graphics board + static uint8_t slot0[] = { + 0x00, 0x00, 0x00, 0x00, '9', '6', '3', 'A', // board + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // eco + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // features + 0xff, 0xff, // reserved + 0x22, 0x00, // family + 0x55, 0xaa, 0x55, 0x00 + }; + + return ((uint8_t *)&slot0)[offset]; +} + +#if 1 +// data is 0x500 = offset 1 +// addr is 0x600 = offset 0 +// 5 0101 = 1 +// 6 0110 = 0 + +// mask 0x100 +WRITE8_MEMBER(interpro_state::interpro_rtc_w) +{ + m_rtc->write(space, offset == 0 ? 1 : 0, data); +} + +READ8_MEMBER(interpro_state::interpro_rtc_r) +{ + return m_rtc->read(space, offset == 0 ? 1 : 0); +} +#endif + +/* +MCGA: 40000xxx - refer iopsysreg.h +00: 0005 +08: CTRL 0010 // control register (hword) +10: ERROR 0400 // error register (hword) +18: 0000 // frcrd register (byte) +20: 0000 // cbsub register (byte) +28: 0088 +30: 001B +38: MEMSIZE 0004 + +*/ + + + + +// driver init +DRIVER_INIT_MEMBER(interpro_state, ip2800) +{ +} + +static ADDRESS_MAP_START(ip2800_map, AS_PROGRAM, 32, interpro_state) + ADDRESS_MAP_UNMAP_LOW + AM_RANGE(0x00000000, 0x00ffffff) AM_RAM // 16M RAM + + AM_RANGE(0x08000000, 0x0800001f) AM_RAM // bogus + + AM_RANGE(0x40000000, 0x4000003f) AM_READWRITE16(mcga_r, mcga_w, 0xffff) + AM_RANGE(0x4f007e00, 0x4f007f7f) AM_RAM // treat SRX GA as ram for now + + AM_RANGE(0x7f000100, 0x7f00011f) AM_DEVICE8(INTERPRO_FDC_TAG, n82077aa_device, map, 0xff) + + AM_RANGE(0x7f000400, 0x7f00040f) AM_DEVREADWRITE8(INTERPRO_SCC1_TAG, scc85C30_device, ba_cd_inv_r, ba_cd_inv_w, 0xff) + AM_RANGE(0x7f000410, 0x7f00041f) AM_DEVREADWRITE8(INTERPRO_SCC2_TAG, scc85230_device, ba_cd_inv_r, ba_cd_inv_w, 0xff) + + AM_RANGE(0x7f000300, 0x7f00030f) AM_READWRITE8(emerald_r, emerald_w, 0xff) + + AM_RANGE(0x7f000500, 0x7f0006ff) AM_READWRITE8(interpro_rtc_r, interpro_rtc_w, 0xff) +//AM_RANGE(0x7F000500, 0x7F000500) AM_DEVREADWRITE8(INTERPRO_RTC_TAG, ds12885_device, read, write, 0xff) AM_MASK(0x100) +//AM_RANGE(0x7F000600, 0x7F000600) AM_DEVREADWRITE8(INTERPRO_RTC_TAG, ds12885_device, read, write, 0xff) AM_MASK(0x100) + +// 7f000768 -> 41 or 42 +// 7f00076c byte reg + + AM_RANGE(0x7F000700, 0x7F00077f) AM_READ(idprom_r) + + AM_RANGE(0x7F0FFF00, 0x7F0FFFFF) AM_DEVICE(INTERPRO_IOGA_TAG, interpro_ioga_device, map) + + AM_RANGE(0x7F100000, 0x7F11FFFF) AM_ROM AM_REGION(INTERPRO_ROM_TAG, 0) + AM_RANGE(0x7F180000, 0x7F1BFFFF) AM_ROM AM_REGION(INTERPRO_EEPROM_TAG, 0) + + AM_RANGE(0x8f007f80, 0x8f007fff) AM_READ(slot0_r) + +ADDRESS_MAP_END + +FLOPPY_FORMATS_MEMBER(interpro_state::floppy_formats) + FLOPPY_PC_FORMAT +FLOPPY_FORMATS_END + +static SLOT_INTERFACE_START(interpro_floppies) + SLOT_INTERFACE("525dd", FLOPPY_525_DD) + SLOT_INTERFACE("35hd", FLOPPY_35_HD) +SLOT_INTERFACE_END + +// input ports +static INPUT_PORTS_START(ip2800) +INPUT_PORTS_END + +static MACHINE_CONFIG_START(ip2800, interpro_state) + MCFG_CPU_ADD(INTERPRO_CPU_TAG, CLIPPER, 70000000) + MCFG_CPU_PROGRAM_MAP(ip2800_map) + MCFG_CPU_IRQ_ACKNOWLEDGE_DEVICE(INTERPRO_IOGA_TAG, interpro_ioga_device, inta_cb) + + MCFG_SCC85C30_ADD(INTERPRO_SCC1_TAG, XTAL_4_9152MHz, 0, 0, 0, 0) + + MCFG_Z80SCC_OUT_TXDA_CB(DEVWRITELINE("rs232a", rs232_port_device, write_txd)) + MCFG_Z80SCC_OUT_TXDB_CB(DEVWRITELINE("rs232b", rs232_port_device, write_txd)) + MCFG_Z80SCC_OUT_INT_CB(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, ir11_w)) + + MCFG_RS232_PORT_ADD("rs232a", default_rs232_devices, nullptr) + MCFG_RS232_RXD_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, rxa_w)) + MCFG_RS232_DCD_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, dcda_w)) + MCFG_RS232_CTS_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, ctsa_w)) + + MCFG_RS232_PORT_ADD("rs232b", default_rs232_devices, "terminal") + MCFG_RS232_RXD_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, rxb_w)) + MCFG_RS232_DCD_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, dcdb_w)) + MCFG_RS232_CTS_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, ctsb_w)) + + MCFG_SCC85230_ADD(INTERPRO_SCC2_TAG, XTAL_4_9152MHz, 0, 0, 0, 0) + + MCFG_DS12885_ADD(INTERPRO_RTC_TAG) + //MCFG_MC146818_IRQ_HANDLER(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, ir9_w)) // FIXME: boot rom doesn't like this + + MCFG_DEVICE_ADD(INTERPRO_LED_TAG, DM9368, 0) + + MCFG_N82077AA_ADD(INTERPRO_FDC_TAG, n82077aa_device::MODE_PS2) + MCFG_UPD765_INTRQ_CALLBACK(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, ir1_w)) + MCFG_UPD765_DRQ_CALLBACK(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, drq)) + MCFG_FLOPPY_DRIVE_ADD("fdc:0", interpro_floppies, "525dd", interpro_state::floppy_formats) + MCFG_FLOPPY_DRIVE_ADD("fdc:1", interpro_floppies, "35hd", interpro_state::floppy_formats) + MCFG_FLOPPY_DRIVE_SOUND(false) + + MCFG_DEVICE_ADD("scsiport", SCSI_PORT, 0) + + MCFG_DEVICE_ADD(INTERPRO_SCSI_TAG, NCR539X, 12500000) + MCFG_LEGACY_SCSI_PORT("scsiport") + MCFG_NCR539X_OUT_IRQ_CB(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, ir0_w)) + + MCFG_INTERPRO_IOGA_ADD(INTERPRO_IOGA_TAG, INPUTLINE(INTERPRO_CPU_TAG, 0)) + + // use callbacks to tell the ioga what the dma read and write methods of each device are + // MCFG_INTERPRO_IOGA_DMA_CALLBACK(channel, n82077aa_device, dma_r, dma_w) + +MACHINE_CONFIG_END + +ROM_START(ip2800) + ROM_REGION(0x0020000, INTERPRO_ROM_TAG, 0) + ROM_SYSTEM_BIOS(0, "IP2830", "IP2830") + ROMX_LOAD("ip2830_rom.bin", 0x00000, 0x20000, CRC(467ce7bd) SHA1(53faee40d5df311f53b24c930e434cbf94a5c4aa), ROM_BIOS(1)) + + ROM_REGION(0x0040000, INTERPRO_EEPROM_TAG, 0) + ROM_LOAD_OPTIONAL("ip2830_eeprom.bin", 0x00000, 0x40000, CRC(a0c0899f) SHA1(dda6fbca81f9885a1a76ca3c25e80463a83a0ef7)) +ROM_END + +/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ +COMP( 1990, ip2800, 0, 0, ip2800, ip2800, interpro_state, ip2800, "Intergraph", "InterPro 2800", MACHINE_NOT_WORKING | MACHINE_NO_SOUND) diff --git a/src/mame/includes/interpro.h b/src/mame/includes/interpro.h new file mode 100644 index 00000000000..d970428feeb --- /dev/null +++ b/src/mame/includes/interpro.h @@ -0,0 +1,90 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#pragma once + +#ifndef INTERPRO_H_ +#define INTERPRO_H_ + +#include "emu.h" +#include "cpu/clipper/clipper.h" + +#include "machine/z80scc.h" +#include "machine/ds128x.h" +#include "bus/rs232/rs232.h" +#include "video/dm9368.h" +#include "machine/upd765.h" +#include "machine/interpro_ioga.h" +#include "machine/ncr539x.h" + +#include "formats/pc_dsk.h" + +#define INTERPRO_CPU_TAG "cpu" +#define INTERPRO_RTC_TAG "rtc" +#define INTERPRO_SCC1_TAG "scc1" +#define INTERPRO_SCC2_TAG "scc2" +#define INTERPRO_ROM_TAG "rom" +#define INTERPRO_EEPROM_TAG "eeprom" +#define INTERPRO_TERMINAL_TAG "terminal" +#define INTERPRO_LED_TAG "led" +#define INTERPRO_FDC_TAG "fdc" +#define INTERPRO_SCSI_TAG "scsi" +#define INTERPRO_IOGA_TAG "ioga" + +// TODO: RTC is actually a DS12887, but the only difference is the 128 byte NVRAM, same as the DS12885 +// TODO: not sure what the LED is, but the DM9368 seems close enough + +class interpro_state : public driver_device +{ +public: + interpro_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag), + m_maincpu(*this, INTERPRO_CPU_TAG), + m_scc1(*this, INTERPRO_SCC1_TAG), + m_scc2(*this, INTERPRO_SCC2_TAG), + m_rtc(*this, INTERPRO_RTC_TAG), + m_led(*this, INTERPRO_LED_TAG), + m_fdc(*this, INTERPRO_FDC_TAG), + m_scsi(*this, INTERPRO_SCSI_TAG), + m_ioga(*this, INTERPRO_IOGA_TAG) + { } + + required_device m_maincpu; + + // FIXME: not sure which one is the escc + required_device m_scc1; + required_device m_scc2; + + required_device m_rtc; + required_device m_led; + required_device m_fdc; + required_device m_scsi; + + required_device m_ioga; + + DECLARE_DRIVER_INIT(ip2800); + + DECLARE_WRITE8_MEMBER(emerald_w); + DECLARE_READ8_MEMBER(emerald_r); + + DECLARE_WRITE16_MEMBER(mcga_w); + DECLARE_READ16_MEMBER(mcga_r); + + DECLARE_WRITE8_MEMBER(interpro_rtc_w); + DECLARE_READ8_MEMBER(interpro_rtc_r); + + DECLARE_READ32_MEMBER(idprom_r); + DECLARE_READ32_MEMBER(slot0_r); + + DECLARE_FLOPPY_FORMATS(floppy_formats); + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + +private: + uint8_t m_emerald_reg[4]; + uint16_t m_mcga[32]; +}; + +#endif \ No newline at end of file diff --git a/src/mame/machine/interpro_ioga.cpp b/src/mame/machine/interpro_ioga.cpp new file mode 100644 index 00000000000..0b1282fcb56 --- /dev/null +++ b/src/mame/machine/interpro_ioga.cpp @@ -0,0 +1,300 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#include "interpro_ioga.h" + +#define VERBOSE 0 +#if VERBOSE +#define LOG_TIMER(...) logerror(__VA_ARGS__) +#define LOG_INTERRUPT(...) logerror(__VA_ARGS__) +#define LOG_IOGA(...) logerror(__VA_ARGS__) +#else +#define LOG_TIMER(...) +#define LOG_INTERRUPT(...) +#define LOG_IOGA(...) +#endif + +// InterPro IOGA +const device_type INTERPRO_IOGA = &device_creator; + +interpro_ioga_device::interpro_ioga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, INTERPRO_IOGA, "InterPro IOGA", tag, owner, clock, "ioga", __FILE__), + m_out_int_func(*this), + m_irq_lines(0) +{ +} + +void interpro_ioga_device::device_start() +{ + // resolve callbacks + m_out_int_func.resolve(); + + m_cpu = machine().device("cpu"); + m_fdc = machine().device("fdc"); + + // allocate timer for DMA controller + m_dma_timer = timer_alloc(IOGA_TIMER_DMA); + m_dma_timer->adjust(attotime::never); +} + +void interpro_ioga_device::device_reset() +{ + m_irq_lines = 0; + m_interrupt = 0; + m_state_drq = 0; +} + +void interpro_ioga_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + LOG_TIMER("interpro_ioga_device::device_timer(id = %d)\n", id); + + switch (id) + { + case IOGA_TIMER_0: + m_timer[0] = 0x80000000; + set_irq_line(14, 1); + break; + case IOGA_TIMER_1: + m_timer[1] = 0x80000000; + set_irq_line(15, 1); + break; + case IOGA_TIMER_3: + m_timer[3] = 0x80000000; + set_irq_line(1, 1); + break; + + case IOGA_TIMER_DMA: + // transfer data from fdc to memory + // TODO: vice-versa + // TODO: get the dma transfer address and count + // TODO: implement multiple dma channels + { + address_space &space = m_cpu->space(AS_PROGRAM); + + space.write_byte(m_fdc_dma[0]++, m_fdc->dma_r()); + if (--m_fdc_dma[2]) + m_dma_timer->adjust(attotime::from_usec(10)); + else + m_dma_timer->adjust(attotime::never); + } + break; + } +} + +DEVICE_ADDRESS_MAP_START(map, 32, interpro_ioga_device) + + AM_RANGE(0x30, 0x3f) AM_READWRITE(fdc_dma_r, fdc_dma_w) + + AM_RANGE(0x5C, 0x81) AM_READWRITE16(icr_r, icr_w, 0xffffffff) + + AM_RANGE(0x8C, 0x8F) AM_READWRITE(timer0_r, timer0_w) + AM_RANGE(0x90, 0x93) AM_READWRITE(timer1_r, timer1_w) + + AM_RANGE(0xA8, 0xAB) AM_READWRITE(timer3_r, timer3_w) +ADDRESS_MAP_END + + +/* +IOGA +00: ethernet remap 003e0480 // ET_82586_BASE_ADDR or IOGA_ETH_REMAP +04 : ethernet map page 00fff4b0 // ET_82586_CNTL_REG or IOGA_ETH_MAPPG +08 : ethernet control 000004b2 // IOGA_ETH_CTL +0C : plotter real address 00000fff +10 : plotter virtual address fffffffc +14 : plotter transfer count 003fffff +18 : plotter control ec000001 +1C : plotter end - of - scanline counter ffffffff +20 : SCSI real address 00000000 +24 : SCSI virtual address 007e96b8 +28 : SCSI transfer count +2C : SCSI control +30 : floppy real address +34 : floppy virtual address +38 : floppy transfer count +3C : floppy control +40 : serial address 0 (003ba298) +44 : serial control 0 (01000000) +48 : serial address 1 (ffffffff) +4C : serial control 1 (01200000) +50 : serial address 2 (ffffffff) +54 : serial control 2 (01200000) + +-- 16 bit +5A : SIB control(00ff) +5C : internal int 3 (timer 2) 00ff +5E : internal int 4 (timer 3) 00ff + +60 : external int 0 (SCSI)0a20 +62 : external int 1 (floppy)0621 +64 : external int 2 (plotter)1622 +66 : external int 3 (SRX / CBUS 0) 0a02 +68 : external int 4 (SRX / CBUS 1) 0e24 +6A : external int 5 (SRX / CBUS 2) 0e25 +6C : external int 6 (VB)0c26 +6E : external int 7 0cff +70 : external int 8 (CBUS 3) 0cff +72 : external int 9 (clock / calendar) 0e29 +74 : external int 10 (clock/SGA) 04fe + +76 : internal int 0 (mouse)0010 +78 : internal int 1 (timer 0) 0011 - 60Hz +7A : internal int 2 (timer 1) 0212 +7C : internal int 5 (serial DMA) 0e13 +7E : external int 11 (serial) 0a01 +80 : external int 12 (Ethernet)162c // IOGA_EXTINT12 + +-- 8 bit +82 : soft int 00 +83 : NMI control 1b + +-- 32 bit +84 : mouse status 00070000 +88 : timer prescaler 05aa06da +8C : timer 0 00000022 +90 : timer 1 00010005 +94 : error address 7f100000 +98 : error cycle type 00001911 // short? + +-- 16 bit +9C: arbiter control 000a // IOGA_ARBCTL + #define ETHC_BR_ENA (1<<0) + #define SCSI_BR_ENA (1<<1) + #define PLT_BR_ENA (1<<2) + #define FLP_BR_ENA (1<<3) + #define SER0_BR_ENA (1<<4) + #define SER1_BR_ENA (1<<5) + #define SER2_BR_ENA (1<<6) + #define ETHB_BR_ENA (1<<7) + #define ETHA_BR_ENA (1<<8) + +9E : I / O base address + +-- 32 bit +A0 : timer 2 count ccc748ec +A4 : timer 2 value ffffffff +A8 : timer 3 bfffffff // timer 3 count +AC : bus timeout 445a445c + +-- 16 bit +B0 : soft int 8 00ff +B2 : soft int 9 00ff +B4 : soft int 10 00ff +B6 : soft int 11 00ff +B8 : soft int 12 00ff +BA : soft int 13 00ff +BC : soft int 14 00ff +BE : soft int 15 00ff + +-- 32 bit +C0 : ethernet address A 403bc002 // IOGA_ETHADDR_A +C4 : ethernet address B 403a68a0 // IOGA_ETHADDR_B +C8 : ethernet address C 4039f088 // IOGA_ETHADDR_C + + (62) = 0x0421 // set floppy interrupts? + (3C) &= 0xfeff ffff // turn off and on again + (3C) |= 0x0100 0000 + (9C) |= 0x0008 + (62) = 0x0621 + + during rom boot, all interrupt vectors point at 7f10249e + + int 16 = prioritized interrupt 16, level 0, number 0, mouse interface + 17 timer 0 +*/ + +void interpro_ioga_device::set_irq_line(int irq, int state) +{ + uint32_t mask = (1 << irq); +#define E_INTRC_INTPEND 0x0100 +#define E_INTRC_EXT_IE 0x0200 +#define E_INTRC_EDGE 0x0400 +#define E_INTRC_NEGPOL 0x0800 +#define E_INTRC_INT_IE 0x1000 + + if (m_vectors[irq] & (E_INTRC_EXT_IE | E_INTRC_INT_IE)) + { + if (state) + { + LOG_INTERRUPT("interpro_ioga_device::set_irq_line(%d, %d)\n", irq, state); + + m_irq_lines |= mask; + m_interrupt = irq; + m_out_int_func(1); + } + else + { + m_irq_lines &= ~mask; + m_out_int_func(0); + } + } + else + LOG_INTERRUPT("ignoring irq %d vector 0x%04x\n", irq, m_vectors[irq]); +} + +IRQ_CALLBACK_MEMBER(interpro_ioga_device::inta_cb) +{ + return m_vectors[m_interrupt]; +} + +WRITE_LINE_MEMBER(interpro_ioga_device::drq) +{ + if (state) + { + // TODO: check if dma is enabled + m_dma_timer->adjust(attotime::from_usec(10)); + } + + m_state_drq = state; +} + + +READ32_MEMBER(interpro_ioga_device::read) +{ + switch (offset) + { +#if 0 + case 0x3C: + // 3C: floppy control + break; + + case 0x9C: + // 9C: arbiter control 000a + break; +#endif + + default: + LOG_IOGA("ioga read from offset = %08x, mask = %08x, pc = %08x\n", offset, mem_mask, space.device().safe_pc()); + return 0xffffffff; + } +} + +void interpro_ioga_device::set_timer(int timer, uint32_t value, device_timer_id id) +{ + m_timer[timer] = value; + if (value & 0x40000000) + //timer_set(attotime::from_usec(value & 0x3fffff), id); + timer_set(attotime::from_usec(500), id); + +} + +WRITE32_MEMBER(interpro_ioga_device::write) +{ + switch (offset) + { + default: + LOG_IOGA("ioga write to offset = 0x%08x, mask = 0x%08x) = 0x%08x, pc = %08x\n", offset, mem_mask, data, space.device().safe_pc()); + break; + } +} + +READ16_MEMBER(interpro_ioga_device::icr_r) +{ + return m_vectors[offset]; +} + +WRITE16_MEMBER(interpro_ioga_device::icr_w) +{ + LOG_INTERRUPT("interrupt vector %d set to 0x%04x at pc 0x%08x\n", offset, data, space.device().safe_pc()); + + m_vectors[offset] = data; +} \ No newline at end of file diff --git a/src/mame/machine/interpro_ioga.h b/src/mame/machine/interpro_ioga.h new file mode 100644 index 00000000000..f592895d191 --- /dev/null +++ b/src/mame/machine/interpro_ioga.h @@ -0,0 +1,100 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#pragma once + +#ifndef INTERPRO_IOGA_H_ +#define INTERPRO_IOGA_H_ + +#include "emu.h" +#include "machine/upd765.h" + +#define MCFG_INTERPRO_IOGA_ADD(_tag, _out_int) \ + MCFG_DEVICE_ADD(_tag, INTERPRO_IOGA, 0) \ + devcb = &interpro_ioga_device::static_set_out_int_callback( *device, DEVCB_##_out_int ); + +class interpro_ioga_device : public device_t +{ +public: + // construction/destruction + interpro_ioga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template static devcb_base &static_set_out_int_callback(device_t &device, _Object object) { return downcast(device).m_out_int_func.set_callback(object); } + + virtual DECLARE_ADDRESS_MAP(map, 8); + + // external interrupt lines + DECLARE_WRITE_LINE_MEMBER(ir0_w) { set_irq_line(2, state); } + DECLARE_WRITE_LINE_MEMBER(ir1_w) { set_irq_line(3, state); } + DECLARE_WRITE_LINE_MEMBER(ir2_w) { set_irq_line(4, state); } + DECLARE_WRITE_LINE_MEMBER(ir3_w) { set_irq_line(5, state); } + DECLARE_WRITE_LINE_MEMBER(ir4_w) { set_irq_line(6, state); } + DECLARE_WRITE_LINE_MEMBER(ir5_w) { set_irq_line(7, state); } + DECLARE_WRITE_LINE_MEMBER(ir6_w) { set_irq_line(8, state); } + DECLARE_WRITE_LINE_MEMBER(ir7_w) { set_irq_line(9, state); } + DECLARE_WRITE_LINE_MEMBER(ir8_w) { set_irq_line(10, state); } + DECLARE_WRITE_LINE_MEMBER(ir9_w) { set_irq_line(11, state); } + DECLARE_WRITE_LINE_MEMBER(ir10_w) { set_irq_line(12, state); } + DECLARE_WRITE_LINE_MEMBER(ir11_w) { set_irq_line(17, state); } + DECLARE_WRITE_LINE_MEMBER(ir12_w) { set_irq_line(18, state); } + + IRQ_CALLBACK_MEMBER(inta_cb); + + DECLARE_WRITE_LINE_MEMBER(drq); + + DECLARE_READ32_MEMBER(read); + DECLARE_WRITE32_MEMBER(write); + + DECLARE_READ32_MEMBER(timer0_r) { return m_timer[0]; }; + DECLARE_READ32_MEMBER(timer1_r) { return m_timer[1]; }; + DECLARE_READ32_MEMBER(timer3_r) { return m_timer[3]; }; + + DECLARE_WRITE32_MEMBER(timer0_w) { set_timer(0, data, IOGA_TIMER_0); } + DECLARE_WRITE32_MEMBER(timer1_w) { set_timer(1, data, IOGA_TIMER_1); } + DECLARE_WRITE32_MEMBER(timer3_w) { set_timer(3, data, IOGA_TIMER_3); } + + DECLARE_READ16_MEMBER(icr_r); + DECLARE_WRITE16_MEMBER(icr_w); + + DECLARE_READ32_MEMBER(fdc_dma_r) { return m_fdc_dma[offset]; }; + DECLARE_WRITE32_MEMBER(fdc_dma_w) { m_fdc_dma[offset] = data; }; + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + cpu_device *m_cpu; + +private: + static const device_timer_id IOGA_TIMER_0 = 0; + static const device_timer_id IOGA_TIMER_1 = 1; + static const device_timer_id IOGA_TIMER_2 = 2; + static const device_timer_id IOGA_TIMER_3 = 3; + + static const device_timer_id IOGA_TIMER_DMA = 4; + + void set_irq_line(int irq, int state); + void set_timer(int timer, uint32_t value, device_timer_id id); + + devcb_write_line m_out_int_func; + + // a hack to get hold of the dma devices + upd765_family_device *m_fdc; + + uint32_t m_irq_lines; + uint8_t m_interrupt; + uint16_t m_vectors[19]; + + uint32_t m_timer[4]; + + emu_timer *m_dma_timer; + uint32_t m_state_drq; + uint32_t m_fdc_dma[4]; +}; + +// device type definition +extern const device_type INTERPRO_IOGA; + +#endif \ No newline at end of file