// license:BSD-3-Clause // copyright-holders:Ville Linde, Angelo Salese, hap /* Motorola MC68HC11 emulator Written by Ville Linde & Angelo Salese TODO: - Interrupts handling is really bare-bones, just to make Hit Poker happy; - Timers are really sketchy as per now, only TOC1 is emulated so far; - Complete opcodes hook-up; - Emulate the MC68HC12 (same as HC11 with a bunch of new opcodes); */ #include "emu.h" #include "debugger.h" #include "mc68hc11.h" enum { HC11_PC = 1, HC11_SP, HC11_A, HC11_B, HC11_IX, HC11_IY }; #define CC_S 0x80 #define CC_X 0x40 #define CC_H 0x20 #define CC_I 0x10 #define CC_N 0x08 #define CC_Z 0x04 #define CC_V 0x02 #define CC_C 0x01 static const int div_tab[4] = { 1, 4, 8, 16 }; const device_type MC68HC11 = &device_creator; mc68hc11_cpu_device::mc68hc11_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : cpu_device(mconfig, MC68HC11, "MC68HC11", tag, owner, clock, "mc68hc11", __FILE__) , m_program_config("program", ENDIANNESS_LITTLE, 8, 16, 0 ) , m_io_config("io", ENDIANNESS_LITTLE, 8, 8, 0) /* defaults it to the HC11M0 version for now (I might strip this down on a later date) */ , m_has_extended_io(1) , m_internal_ram_size(1280) , m_init_value(0x01) { } offs_t mc68hc11_cpu_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) { extern CPU_DISASSEMBLE( hc11 ); return CPU_DISASSEMBLE_NAME(hc11)(this, buffer, pc, oprom, opram, options); } #define HC11OP(XX) mc68hc11_cpu_device::hc11_##XX /*****************************************************************************/ /* Internal registers */ UINT8 mc68hc11_cpu_device::hc11_regs_r(UINT32 address) { int reg = address & 0xff; switch(reg) { case 0x00: /* PORTA */ return m_io->read_byte(MC68HC11_IO_PORTA); case 0x01: /* DDRA */ return 0; case 0x02: /* PIOC */ return 0; case 0x03: /* PORTC */ return m_io->read_byte(MC68HC11_IO_PORTC); case 0x04: /* PORTB */ return m_io->read_byte(MC68HC11_IO_PORTB); case 0x08: /* PORTD */ return m_io->read_byte(MC68HC11_IO_PORTD); case 0x09: /* DDRD */ return 0; case 0x0a: /* PORTE */ return m_io->read_byte(MC68HC11_IO_PORTE); case 0x0e: /* TCNT */ return m_tcnt >> 8; case 0x0f: return m_tcnt & 0xff; case 0x16: /* TOC1 */ return m_toc1 >> 8; case 0x17: return m_toc1 & 0xff; case 0x23: return m_tflg1; case 0x28: /* SPCR1 */ return 0; case 0x30: /* ADCTL */ return 0x80; case 0x31: /* ADR1 */ { if (m_adctl & 0x10) { return m_io->read_byte((m_adctl & 0x4) + MC68HC11_IO_AD0); } else { return m_io->read_byte((m_adctl & 0x7) + MC68HC11_IO_AD0); } } case 0x32: /* ADR2 */ { if (m_adctl & 0x10) { return m_io->read_byte((m_adctl & 0x4) + MC68HC11_IO_AD1); } else { return m_io->read_byte((m_adctl & 0x7) + MC68HC11_IO_AD0); } } case 0x33: /* ADR3 */ { if (m_adctl & 0x10) { return m_io->read_byte((m_adctl & 0x4) + MC68HC11_IO_AD2); } else { return m_io->read_byte((m_adctl & 0x7) + MC68HC11_IO_AD0); } } case 0x34: /* ADR4 */ { if (m_adctl & 0x10) { return m_io->read_byte((m_adctl & 0x4) + MC68HC11_IO_AD3); } else { return m_io->read_byte((m_adctl & 0x7) + MC68HC11_IO_AD0); } } case 0x38: /* OPT2 */ return 0; case 0x70: /* SCBDH */ return 0; case 0x71: /* SCBDL */ return 0; case 0x72: /* SCCR1 */ return 0; case 0x73: /* SCCR2 */ return 0; case 0x74: /* SCSR1 */ return 0x40; case 0x7c: /* PORTH */ return m_io->read_byte(MC68HC11_IO_PORTH); case 0x7e: /* PORTG */ return m_io->read_byte(MC68HC11_IO_PORTG); case 0x7f: /* DDRG */ return 0; case 0x88: /* SPCR2 */ return 0; case 0x89: /* SPSR2 */ return 0x80; case 0x8a: /* SPDR2 */ return m_io->read_byte(MC68HC11_IO_SPI2_DATA); case 0x8b: /* OPT4 */ return 0; } logerror("HC11: regs_r %02X\n", reg); return 0; // Dummy } void mc68hc11_cpu_device::hc11_regs_w(UINT32 address, UINT8 value) { int reg = address & 0xff; switch(reg) { case 0x00: /* PORTA */ m_io->write_byte(MC68HC11_IO_PORTA, value); return; case 0x01: /* DDRA */ //osd_printf_debug("HC11: ddra = %02X\n", value); return; case 0x03: /* PORTC */ m_io->write_byte(MC68HC11_IO_PORTC, value); return; case 0x04: /* PORTC */ m_io->write_byte(MC68HC11_IO_PORTB, value); return; case 0x08: /* PORTD */ m_io->write_byte(MC68HC11_IO_PORTD, value); //mask & 0x3f? return; case 0x09: /* DDRD */ //osd_printf_debug("HC11: ddrd = %02X\n", value); return; case 0x0a: /* PORTE */ m_io->write_byte(MC68HC11_IO_PORTE, value); return; case 0x0e: /* TCNT */ case 0x0f: logerror("HC11: TCNT register write %02x %02x!\n",address,value); return; case 0x16: /* TOC1 */ /* TODO: inhibit for one bus cycle */ m_toc1 = (value << 8) | (m_toc1 & 0xff); return; case 0x17: m_toc1 = (value & 0xff) | (m_toc1 & 0xff00); return; case 0x22: /* TMSK1 */ m_tmsk1 = value; return; case 0x23: m_tflg1 &= ~value; return; case 0x24: /* TMSK2 */ m_pr = value & 3; return; case 0x28: /* SPCR1 */ return; case 0x30: /* ADCTL */ m_adctl = value; return; case 0x38: /* OPT2 */ return; case 0x39: /* OPTION */ return; case 0x3a: /* COPRST (watchdog) */ return; case 0x3d: /* INIT */ { int reg_page = value & 0xf; int ram_page = (value >> 4) & 0xf; if (reg_page == ram_page) { m_reg_position = reg_page << 12; m_ram_position = (ram_page << 12) + ((m_has_extended_io) ? 0x100 : 0x80); } else { m_reg_position = reg_page << 12; m_ram_position = ram_page << 12; } return; } case 0x3f: /* CONFIG */ return; case 0x70: /* SCBDH */ return; case 0x71: /* SCBDL */ return; case 0x72: /* SCCR1 */ return; case 0x73: /* SCCR2 */ return; case 0x77: /* SCDRL */ return; case 0x7c: /* PORTH */ m_io->write_byte(MC68HC11_IO_PORTH, value); return; case 0x7d: /* DDRH */ //osd_printf_debug("HC11: ddrh = %02X at %04X\n", value, m_pc); return; case 0x7e: /* PORTG */ m_io->write_byte(MC68HC11_IO_PORTG, value); return; case 0x7f: /* DDRG */ //osd_printf_debug("HC11: ddrg = %02X at %04X\n", value, m_pc); return; case 0x88: /* SPCR2 */ return; case 0x89: /* SPSR2 */ return; case 0x8a: /* SPDR2 */ m_io->write_byte(MC68HC11_IO_SPI2_DATA, value); return; case 0x8b: /* OPT4 */ return; } logerror("HC11: regs_w %02X, %02X\n", reg, value); } /*****************************************************************************/ UINT8 mc68hc11_cpu_device::FETCH() { return m_direct->read_decrypted_byte(m_pc++); } UINT16 mc68hc11_cpu_device::FETCH16() { UINT16 w; w = (m_direct->read_decrypted_byte(m_pc) << 8) | (m_direct->read_decrypted_byte(m_pc+1)); m_pc += 2; return w; } UINT8 mc68hc11_cpu_device::READ8(UINT32 address) { if(address >= m_reg_position && address < m_reg_position+(m_has_extended_io ? 0x100 : 0x40)) { return hc11_regs_r(address); } else if(address >= m_ram_position && address < m_ram_position+m_internal_ram_size) { return m_internal_ram[address-m_ram_position]; } return m_program->read_byte(address); } void mc68hc11_cpu_device::WRITE8(UINT32 address, UINT8 value) { if(address >= m_reg_position && address < m_reg_position+(m_has_extended_io ? 0x100 : 0x40)) { hc11_regs_w(address, value); return; } else if(address >= m_ram_position && address < m_ram_position+m_internal_ram_size) { m_internal_ram[address-m_ram_position] = value; return; } m_program->write_byte(address, value); } UINT16 mc68hc11_cpu_device::READ16(UINT32 address) { return (READ8(address) << 8) | (READ8(address+1)); } void mc68hc11_cpu_device::WRITE16(UINT32 address, UINT16 value) { WRITE8(address+0, (value >> 8) & 0xff); WRITE8(address+1, (value >> 0) & 0xff); } /*****************************************************************************/ #include "hc11ops.inc" #include "hc11ops.h" void mc68hc11_cpu_device::device_start() { int i; /* clear the opcode tables */ for(i=0; i < 256; i++) { hc11_optable[i] = &HC11OP(invalid); hc11_optable_page2[i] = &HC11OP(invalid); hc11_optable_page3[i] = &HC11OP(invalid); hc11_optable_page4[i] = &HC11OP(invalid); } /* fill the opcode tables */ for(i=0; i < sizeof(hc11_opcode_list)/sizeof(hc11_opcode_list_struct); i++) { switch(hc11_opcode_list[i].page) { case 0x00: hc11_optable[hc11_opcode_list[i].opcode] = hc11_opcode_list[i].handler; break; case 0x18: hc11_optable_page2[hc11_opcode_list[i].opcode] = hc11_opcode_list[i].handler; break; case 0x1A: hc11_optable_page3[hc11_opcode_list[i].opcode] = hc11_opcode_list[i].handler; break; case 0xCD: hc11_optable_page4[hc11_opcode_list[i].opcode] = hc11_opcode_list[i].handler; break; } } m_internal_ram.resize(m_internal_ram_size); m_program = &space(AS_PROGRAM); m_direct = &m_program->direct(); m_io = &space(AS_IO); save_item(NAME(m_pc)); save_item(NAME(m_ix)); save_item(NAME(m_iy)); save_item(NAME(m_sp)); save_item(NAME(m_ppc)); save_item(NAME(m_ccr)); save_item(NAME(m_d.d8.a)); save_item(NAME(m_d.d8.b)); save_item(NAME(m_adctl)); save_item(NAME(m_ad_channel)); save_item(NAME(m_ram_position)); save_item(NAME(m_reg_position)); save_item(NAME(m_irq_state)); save_item(NAME(m_has_extended_io)); save_item(NAME(m_internal_ram_size)); save_item(NAME(m_init_value)); save_item(NAME(m_internal_ram)); save_item(NAME(m_wait_state)); save_item(NAME(m_stop_state)); save_item(NAME(m_tflg1)); save_item(NAME(m_tmsk1)); save_item(NAME(m_toc1)); save_item(NAME(m_tcnt)); // save_item(NAME(m_por)); save_item(NAME(m_pr)); save_item(NAME(m_frc_base)); m_pc = 0; m_d.d16 = 0; m_ix = 0; m_iy = 0; m_sp = 0; m_ppc = 0; m_adctl = 0; m_ad_channel = 0; m_irq_state[0] = m_irq_state[1] = 0; m_ram_position = 0; m_reg_position = 0; m_tflg1 = 0; m_tmsk1 = 0; state_add( HC11_PC, "PC", m_pc).formatstr("%04X"); state_add( HC11_SP, "SP", m_sp).formatstr("%04X"); state_add( HC11_A, "A", m_d.d8.a).formatstr("%02X"); state_add( HC11_B, "B", m_d.d8.b).formatstr("%02X"); state_add( HC11_IX, "IX", m_ix).formatstr("%04X"); state_add( HC11_IY, "IY", m_iy).formatstr("%04X"); state_add( STATE_GENPC, "GENPC", m_pc).noshow(); state_add( STATE_GENFLAGS, "GENFLAGS", m_ccr).formatstr("%8s").noshow(); m_icountptr = &m_icount; } void mc68hc11_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) { switch (entry.index()) { case STATE_GENFLAGS: strprintf(str, "%c%c%c%c%c%c%c%c", (m_ccr & CC_S) ? 'S' : '.', (m_ccr & CC_X) ? 'X' : '.', (m_ccr & CC_H) ? 'H' : '.', (m_ccr & CC_I) ? 'I' : '.', (m_ccr & CC_N) ? 'N' : '.', (m_ccr & CC_Z) ? 'Z' : '.', (m_ccr & CC_V) ? 'V' : '.', (m_ccr & CC_C) ? 'C' : '.'); break; } } void mc68hc11_cpu_device::device_reset() { m_pc = READ16(0xfffe); m_wait_state = 0; m_stop_state = 0; m_ccr = CC_X | CC_I | CC_S; hc11_regs_w(0x3d,m_init_value); m_toc1 = 0xffff; m_tcnt = 0xffff; // m_por = 1; // for first timer overflow / compare stuff m_pr = 3; // timer prescale m_frc_base = 0; } /* IRQ table vectors: 0xffd6: SCI 0xffd8: SPI 0xffda: Pulse Accumulator Input Edge 0xffdc: Pulse Accumulator Overflow 0xffde: Timer Overflow 0xffe0: Timer Output Capture 5 0xffe2: Timer Output Capture 4 0xffe4: Timer Output Capture 3 0xffe6: Timer Output Capture 2 0xffe8: Timer Output Capture 1 0xffea: Timer Input Capture 3 0xffec: Timer Input Capture 2 0xffee: Timer Input Capture 1 0xfff0: Real Time Int 0xfff2: IRQ 0xfff4: XIRQ 0xfff6: SWI (Trap IRQ) 0xfff8: Illegal Opcode (NMI) 0xfffa: CO-Processor Fail 0xfffc: Clock Monitor 0xfffe: RESET */ void mc68hc11_cpu_device::check_irq_lines() { if( m_irq_state[MC68HC11_IRQ_LINE]!=CLEAR_LINE && (!(m_ccr & CC_I)) ) { UINT16 pc_vector; if(m_wait_state == 0) { PUSH16(m_pc); PUSH16(m_iy); PUSH16(m_ix); PUSH8(REG_A); PUSH8(REG_B); PUSH8(m_ccr); } pc_vector = READ16(0xfff2); SET_PC(pc_vector); m_ccr |= CC_I; //irq taken, mask the flag if(m_wait_state == 1) { m_wait_state = 2; } if(m_stop_state == 1) { m_stop_state = 2; } standard_irq_callback(MC68HC11_IRQ_LINE); } /* check timers here */ { int divider = div_tab[m_pr & 3]; UINT64 cur_time = total_cycles(); UINT32 add = (cur_time - m_frc_base) / divider; if (add > 0) { for(UINT32 i=0;i 0) { UINT8 op; check_irq_lines(); m_ppc = m_pc; debugger_instruction_hook(this, m_pc); op = FETCH(); (this->*hc11_optable[op])(); } }