summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/mc68hc11/mc68hc11.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/mc68hc11/mc68hc11.c')
-rw-r--r--src/devices/cpu/mc68hc11/mc68hc11.c599
1 files changed, 599 insertions, 0 deletions
diff --git a/src/devices/cpu/mc68hc11/mc68hc11.c b/src/devices/cpu/mc68hc11/mc68hc11.c
new file mode 100644
index 00000000000..f05d9126334
--- /dev/null
+++ b/src/devices/cpu/mc68hc11/mc68hc11.c
@@ -0,0 +1,599 @@
+// 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::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_byte(m_pc++);
+}
+
+UINT16 mc68hc11_cpu_device::FETCH16()
+{
+ UINT16 w;
+ w = (m_direct->read_byte(m_pc) << 8) | (m_direct->read_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<add;i++)
+ {
+ m_tcnt++;
+ if(m_tcnt == m_toc1)
+ {
+ m_tflg1 |= 0x80;
+ m_irq_state[MC68HC11_TOC1_LINE] = ASSERT_LINE;
+ }
+ }
+
+ m_frc_base = cur_time;
+ }
+ }
+
+ if( m_irq_state[MC68HC11_TOC1_LINE]!=CLEAR_LINE && (!(m_ccr & CC_I)) && m_tmsk1 & 0x80)
+ {
+ 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(0xffe8);
+ 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_TOC1_LINE);
+ m_irq_state[MC68HC11_TOC1_LINE] = CLEAR_LINE; // auto-ack irq
+ }
+
+}
+
+void mc68hc11_cpu_device::execute_set_input(int inputnum, int state)
+{
+ m_irq_state[inputnum] = state;
+ if (state == CLEAR_LINE) return;
+ check_irq_lines();
+}
+
+void mc68hc11_cpu_device::execute_run()
+{
+ while(m_icount > 0)
+ {
+ UINT8 op;
+
+ check_irq_lines();
+
+ m_ppc = m_pc;
+ debugger_instruction_hook(this, m_pc);
+
+ op = FETCH();
+ (this->*hc11_optable[op])();
+ }
+}